Skip to content

How to Migrate OpenClaw Plugins from extension-api to plugin-sdk

I ran openclaw plugins install my-tool and everything seemed fine. But when I tried to start OpenClaw, it crashed immediately with a cryptic error about missing modules.

The culprit? A breaking change in OpenClaw’s plugin SDK import paths that has no compatibility layer.

The Error That Stopped Everything

After updating to the latest OpenClaw version, my plugins started failing with:

Terminal Output
Error: Cannot find module 'openclaw/extension-api'
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1077:15)
at Module.require (node:internal/modules/cjs/loader:1225:19)

This wasn’t a deprecation warning. This wasn’t a helpful migration message. My plugins were completely broken.

What Actually Changed

OpenClaw migrated from openclaw/extension-api to openclaw/plugin-sdk/* as the import path for plugin development.

import-comparison.ts
// Before (deprecated, will break)
import { Tool } from 'openclaw/extension-api'
// After (new SDK path)
import { Tool } from 'openclaw/plugin-sdk'

This isn’t just a rename. It’s a fundamental restructuring with no backwards compatibility and no migration tooling. Plugins importing from the old path will hard-fail on launch, not gracefully degrade.

Why This Matters

I spent 30 minutes thinking I had corrupted my installation. The error message doesn’t tell you what changed. It just fails.

Impact Summary
┌─────────────────────┬──────────────────────┬─────────────────────┐
│ Impact Area │ Before │ After │
├─────────────────────┼──────────────────────┼─────────────────────┤
│ Plugin loading │ Graceful import │ Hard-fail on launch │
│ Error message │ May show deprecation │ Immediate crash │
│ Fix complexity │ Simple rename │ Requires code change│
│ Third-party plugins │ Work normally │ Broken until updated│
└─────────────────────┴──────────────────────┴─────────────────────┘

Discovery Process: How I Found the Problem

First, I checked if it was a cache issue:

clear-cache.sh
openclaw doctor --fix
rm -rf ~/.openclaw/plugins/*

Still broken. Then I tried reinstalling:

reinstall-plugin.sh
openclaw plugins install my-tool

The install succeeded, but the error persisted. That’s when I realized: the plugin code itself was using the old import path.

I checked the plugin source:

check-imports.sh
grep -r "extension-api" ~/.openclaw/plugins/my-tool/

And there it was. Multiple files importing from openclaw/extension-api.

The Three Migration Paths

Path 1: Check for Plugin Updates (Easiest)

The plugin maintainer may have already fixed this:

check-updates.sh
# Check if there's a newer version
npm view my-tool versions --json
# Check the repository for migration branches
gh api repos/owner/my-tool/branches --jq '.[].name' | grep -i migrat
# Reinstall if updated
openclaw plugins install my-tool

This worked for some of my plugins. The maintainers had already published updates.

Path 2: Pin OpenClaw Version (Workaround)

For plugins without updates, pinning to an older OpenClaw version works:

pin-version.sh
# Pin to the last working version
npm install -g [email protected]
# Or use npx for one-off runs
npx [email protected] plugins list

This is a temporary fix, but it let me continue working while waiting for plugin updates.

Path 3: Fork and Fix (Most Control)

For private plugins or slow-to-update maintainers, I had to fix it myself:

find-and-fix.sh
# Find all affected imports
grep -rn "openclaw/extension-api" ./src
# Using sed to replace (backup first!)
find ./src -name "*.ts" -exec sed -i.bak 's|openclaw/extension-api|openclaw/plugin-sdk|g' {} +
# Or manually update each file

Before (broken):

src/index.ts (before)
import { Tool, Agent } from 'openclaw/extension-api'
export const myTool: Tool = {
name: 'my-tool',
description: 'A sample tool',
execute: async (input) => {
return { result: 'done' }
}
}

After (working):

src/index.ts (after)
import { Tool, Agent } from 'openclaw/plugin-sdk'
export const myTool: Tool = {
name: 'my-tool',
description: 'A sample tool',
execute: async (input) => {
return { result: 'done' }
}
}

Bonus Breaking Changes

While debugging, I discovered two more changes:

1. ClawHub is Now the Default Store

install-sources.sh
# Default: ClawHub first, then npm
openclaw plugins install my-tool
# Force npm source
openclaw plugins install my-tool --source npm
# Force ClawHub source
openclaw plugins install my-tool --source clawhub

2. Private Registry Installs Require Explicit Flag

private-registry.sh
# Old way (may fail)
openclaw plugins install my-private-plugin
# New way (explicit source)
openclaw plugins install my-private-plugin --source npm

This caused issues with my company’s private plugin registry until I added the --source npm flag.

My Migration Checklist

After fixing all my plugins, I created this checklist for future reference:

Migration Checklist
□ List all installed plugins: openclaw plugins list
□ Check each plugin repo for migration branch or updated version
□ Update plugins from ClawHub or npm as available
□ For private plugins, check imports for openclaw/extension-api
□ Update imports to openclaw/plugin-sdk
□ Test each plugin individually after update
□ Use openclaw doctor --fix to clean up stale references
□ Update private npm registry installs to use --source npm
□ Document any plugins that needed manual updates

How to Audit Your Plugins

Before updating OpenClaw, run this to identify at-risk plugins:

audit-plugins.sh
# List installed plugins
openclaw plugins list
# Check plugin source for old imports
for plugin in $(openclaw plugins list --json | jq -r '.[].name'); do
echo "Checking $plugin..."
npm view "$plugin" repository.url 2>/dev/null
done
# Or check locally installed plugins
grep -r "openclaw/extension-api" ~/.openclaw/plugins/

If you see any matches, don’t update OpenClaw until you’ve verified the plugins have been migrated.

What I Learned

  1. Breaking changes without migration tooling cost real time. I lost 2 hours to this across all my projects.
  2. Check the changelog before updating. This was documented, but I didn’t check.
  3. Pin versions in production. package-lock.json or equivalent isn’t enough when tools update globally.
  4. The error message didn’t help. A simple “This import path was migrated in version X.Y.Z” would have saved me time.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments