How to Migrate OpenClaw Plugins to the New Plugin SDK (2026)
I upgraded to OpenClaw 2026.3.22-beta.1 and my plugin stopped working immediately. The error was clear but frustrating:
Error: Cannot find module 'openclaw/extension-api' at PluginLoader.loadPlugin (src/plugin-loader.ts:142) at async PluginManager.initialize (src/plugin-manager.ts:89) at async OpenClaw.startup (src/index.ts:45)This wasn’t a deprecation warning. The entire openclaw/extension-api module was gone. No compatibility shim, no fallback, nothing. Let me walk through how I fixed this.
The Problem: A Breaking Change With No Grace Period
The OpenClaw team described this as “the most painful breaking change for plugin authors.” They weren’t kidding.
Here’s what my plugin code looked like before:
import { commands, window, workspace } from 'openclaw/extension-api'
export function activate() { commands.registerCommand('myPlugin.hello', () => { window.showInformationMessage('Hello from my plugin!') })
const config = workspace.getConfiguration('myPlugin') console.log('Config loaded:', config)}This code had worked for months. After the upgrade, it didn’t just warn me or suggest migration - it threw a hard error and refused to load.
Step 1: Read the Migration Guide (Don’t Skip This)
Before touching any code, I went to the official migration guide at docs.openclaw.ai/plugins/sdk-migration. This is critical because the SDK changes go beyond just import paths.
Key things I learned from the guide:
- The new SDK uses modular imports from
openclaw/plugin-sdk/* - Bundled plugins require an injected runtime for host-side operations
- Some API signatures changed, not just the import paths
Step 2: Find All Deprecated Imports
I searched my plugin codebase for all references to the old API:
grep -r "openclaw/extension-api" my-plugin/src/Output:
my-plugin/src/index.ts:1:import { commands, window, workspace } from 'openclaw/extension-api'my-plugin/src/providers.ts:2:import { Range, Position, TextDocument } from 'openclaw/extension-api'my-plugin/src/utils.ts:3:import { ConfigurationTarget, WorkspaceConfiguration } from 'openclaw/extension-api'Three files. Not too bad, but I needed to update each one.
Step 3: Update Imports to the New SDK
The new plugin SDK uses a modular structure. Instead of importing everything from one module, I imported from specific submodules.
Before (Broken):
import { commands, window, workspace } from 'openclaw/extension-api'
export function activate() { commands.registerCommand('myPlugin.hello', () => { window.showInformationMessage('Hello from my plugin!') })
const config = workspace.getConfiguration('myPlugin') console.log('Config loaded:', config)}After (Fixed):
import { commands } from 'openclaw/plugin-sdk/commands'import { window } from 'openclaw/plugin-sdk/window'import { workspace } from 'openclaw/plugin-sdk/workspace'
export function activate() { commands.registerCommand('myPlugin.hello', () => { window.showInformationMessage('Hello from my plugin!') })
const config = workspace.getConfiguration('myPlugin') console.log('Config loaded:', config)}Step 4: Handle Type Imports
My providers file had type imports that also needed updating:
import { Range, Position, TextDocument } from 'openclaw/extension-api'
export class MyDocumentSymbolProvider { provideDocumentSymbols(document: TextDocument): Range[] { const position = new Position(0, 0) const range = new Range(position, position) return [range] }}import { Range, Position, TextDocument } from 'openclaw/plugin-sdk/types'
export class MyDocumentSymbolProvider { provideDocumentSymbols(document: TextDocument): Range[] { const position = new Position(0, 0) const range = new Range(position, position) return [range] }}Step 5: Bundled Plugins Need Runtime Injection
My plugin wasn’t bundled, but if yours is, you need to handle it differently. Bundled plugins must use an injected runtime for host-side operations.
Here’s the pattern for bundled plugins:
import type { PluginRuntime } from 'openclaw/plugin-sdk/runtime'
export function initialize(runtime: PluginRuntime) { // Use runtime for host-side operations instead of direct imports runtime.commands.registerCommand('bundledPlugin.hello', () => { runtime.window.showInformationMessage('Hello from bundled plugin!') })
const config = runtime.workspace.getConfiguration('bundledPlugin') console.log('Config loaded:', config)}The runtime is injected by OpenClaw when loading bundled plugins. This is a security and isolation improvement in the new architecture.
Step 6: Run Type Checks
After updating all imports, I ran the TypeScript compiler:
cd my-plugin && npm run type-checkOutput:
src/utils.ts:15:23 - error TS2339: Property 'update' does not exist on type 'WorkspaceConfiguration'. Property 'update' was moved to 'Configuration' interface.
15 config.update('setting', newValue) ~~~~~~
Found 1 error.Ah, an API signature change. The update method moved from WorkspaceConfiguration to a separate Configuration interface.
Fix:
import { workspace } from 'openclaw/plugin-sdk/workspace'import { Configuration } from 'openclaw/plugin-sdk/configuration'
export async function updateSetting(key: string, value: unknown) { const config = new Configuration(workspace.getConfiguration('myPlugin')) await config.update(key, value)}This is why reading the migration guide first matters - there were API surface changes beyond just import paths.
Step 7: Test Everything
I tested each plugin feature:
npm run testPASS src/index.test.ts activate + registers hello command (5ms) + loads configuration (2ms)
PASS src/providers.test.ts MyDocumentSymbolProvider + provides document symbols (3ms)
Test Suites: 2 passed, 2 totalTests: 3 passed, 3 totalAll tests passed. But I also manually tested in OpenClaw to make sure the runtime behavior matched.
Step 8: Update Version and Publish
I updated my package.json:
{ "name": "my-openclaw-plugin", "version": "2.0.0", "engines": { "openclaw": ">=2026.3.22" }}Then published to ClawHub:
openclaw publish --hub clawhubPublishing [email protected] to ClawHub...+ Successfully published!View at: https://clawhub.io/plugins/my-openclaw-pluginMigration Checklist
Here’s the complete checklist I followed:
- Read docs.openclaw.ai/plugins/sdk-migration in full
- Identify all files using
openclaw/extension-apiimports - Update imports to
openclaw/plugin-sdk/* - Update bundled plugins to use injected runtime (if applicable)
- Run type checks and fix any API changes
- Test all plugin functionality
- Update plugin version number
- Update plugin documentation
- Publish to ClawHub
- Notify users of the update
What I Wish I Had Known
-
Don’t rush. The migration guide exists for a reason. Read it completely before touching code.
-
Check for API signature changes. It’s not just import paths that changed. Some method signatures moved or changed behavior.
-
Test in staging first. Don’t publish until you’ve manually verified everything works.
-
Check your dependencies. If your plugin depends on other community plugins, verify they’ve also migrated.
-
Publish to ClawHub. The OpenClaw team recommends ClawHub for community plugins. It increases visibility and helps users discover compatible plugins.
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