openclaw - 💡(How to fix) Fix External plugins fail to resolve openclaw/plugin-sdk/* imports — alias map missing argv1 fallback [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#53685Fetched 2026-04-08 01:24:52
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
1
Author
Participants
Timeline (top)
cross-referenced ×2commented ×1subscribed ×1

External/community plugins installed at ~/.openclaw/extensions/ fail to load with:

Cannot find module 'openclaw/plugin-sdk/plugin-entry'
Require stack:
- /home/user/.openclaw/extensions/my-plugin/index.ts

This affects all non-bundled plugins installed via openclaw plugins install (both local path and ClawHub sources).

Error Message

Create a minimal provider plugin

mkdir /tmp/test-plugin && cd /tmp/test-plugin cat > package.json << 'PKGEOF' { "name": "test-plugin", "version": "1.0.0", "type": "module", "openclaw": { "extensions": ["./index.ts"], "providers": ["test"] } } PKGEOF cat > index.ts << 'TSEOF' import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; export default definePluginEntry({ id: "test", name: "Test", register() {} }); TSEOF

Install and inspect

openclaw plugins install /tmp/test-plugin openclaw plugins inspect test-plugin

→ ERROR: Cannot find module 'openclaw/plugin-sdk/plugin-entry'

Root Cause

In src/plugins/loader.ts:728, buildPluginLoaderAliasMap(modulePath) is called with only the plugin's source path. Inside sdk-alias.ts, resolveLoaderPluginSdkPackageRoot() tries to find the OpenClaw package root by walking up from the plugin's directory:

~/.openclaw/extensions/my-plugin/ → ~/.openclaw/extensions/ → ~/.openclaw/ → ~/ → /

None of these contain a package.json with "name": "openclaw", so the package root resolution fails and the alias map is returned empty. Without aliases, jiti treats openclaw/plugin-sdk/* as bare Node specifiers that cannot be resolved from the plugin's directory.

Bundled extensions (under extensions/ inside the OpenClaw package) work fine because walking up from their directory reaches the OpenClaw package.json.

The resolveLoaderPluginSdkPackageRoot function (sdk-alias.ts:109-129) has fallback paths using argv1 and moduleUrl that would resolve the package root via the OpenClaw binary path. However, buildPluginLoaderAliasMap (sdk-alias.ts:288) does not pass these parameters through — they remain undefined, so the fallbacks are never tried.

Fix Action

Fix / Workaround

Workaround: Symlink the global OpenClaw package into the plugin's node_modules:

  1. Provider env vars: Allow external plugins' providerAuthEnvVars from their openclaw.plugin.json manifests to be merged into the env var candidate map at runtime, rather than relying solely on the compile-time generated file.

Code Example

Cannot find module 'openclaw/plugin-sdk/plugin-entry'
Require stack:
- /home/user/.openclaw/extensions/my-plugin/index.ts

---

~/.openclaw/extensions/my-plugin/~/.openclaw/extensions/~/.openclaw/~//

---

# Create a minimal provider plugin
mkdir /tmp/test-plugin && cd /tmp/test-plugin
cat > package.json << 'PKGEOF'
{ "name": "test-plugin", "version": "1.0.0", "type": "module",
  "openclaw": { "extensions": ["./index.ts"], "providers": ["test"] } }
PKGEOF
cat > index.ts << 'TSEOF'
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
export default definePluginEntry({ id: "test", name: "Test", register() {} });
TSEOF

# Install and inspect
openclaw plugins install /tmp/test-plugin
openclaw plugins inspect test-plugin
# → ERROR: Cannot find module 'openclaw/plugin-sdk/plugin-entry'

---

mkdir -p ~/.openclaw/extensions/test-plugin/node_modules
ln -s "$(dirname "$(readlink -f "$(which openclaw)")")" \
  ~/.openclaw/extensions/test-plugin/node_modules/openclaw
RAW_BUFFERClick to expand / collapse

Summary

External/community plugins installed at ~/.openclaw/extensions/ fail to load with:

Cannot find module 'openclaw/plugin-sdk/plugin-entry'
Require stack:
- /home/user/.openclaw/extensions/my-plugin/index.ts

This affects all non-bundled plugins installed via openclaw plugins install (both local path and ClawHub sources).

Root Cause

In src/plugins/loader.ts:728, buildPluginLoaderAliasMap(modulePath) is called with only the plugin's source path. Inside sdk-alias.ts, resolveLoaderPluginSdkPackageRoot() tries to find the OpenClaw package root by walking up from the plugin's directory:

~/.openclaw/extensions/my-plugin/ → ~/.openclaw/extensions/ → ~/.openclaw/ → ~/ → /

None of these contain a package.json with "name": "openclaw", so the package root resolution fails and the alias map is returned empty. Without aliases, jiti treats openclaw/plugin-sdk/* as bare Node specifiers that cannot be resolved from the plugin's directory.

Bundled extensions (under extensions/ inside the OpenClaw package) work fine because walking up from their directory reaches the OpenClaw package.json.

The resolveLoaderPluginSdkPackageRoot function (sdk-alias.ts:109-129) has fallback paths using argv1 and moduleUrl that would resolve the package root via the OpenClaw binary path. However, buildPluginLoaderAliasMap (sdk-alias.ts:288) does not pass these parameters through — they remain undefined, so the fallbacks are never tried.

Second Issue: resolveProviderApiKey doesn't work for external provider plugins

resolveProviderApiKey() looks up env var candidates from bundled-provider-auth-env-vars.generated.ts, which is auto-generated from bundled extension manifests only. External plugins are not in this map, so their declared envVars (e.g., NOVITA_API_KEY) are never resolved even when present in process.env or ~/.openclaw/.env.

Reproduction

# Create a minimal provider plugin
mkdir /tmp/test-plugin && cd /tmp/test-plugin
cat > package.json << 'PKGEOF'
{ "name": "test-plugin", "version": "1.0.0", "type": "module",
  "openclaw": { "extensions": ["./index.ts"], "providers": ["test"] } }
PKGEOF
cat > index.ts << 'TSEOF'
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
export default definePluginEntry({ id: "test", name: "Test", register() {} });
TSEOF

# Install and inspect
openclaw plugins install /tmp/test-plugin
openclaw plugins inspect test-plugin
# → ERROR: Cannot find module 'openclaw/plugin-sdk/plugin-entry'

Workaround: Symlink the global OpenClaw package into the plugin's node_modules:

mkdir -p ~/.openclaw/extensions/test-plugin/node_modules
ln -s "$(dirname "$(readlink -f "$(which openclaw)")")" \
  ~/.openclaw/extensions/test-plugin/node_modules/openclaw

Suggested Fix

  1. Alias resolution: Pass process.argv[1] (or import.meta.url of the loader module) through to buildPluginLoaderAliasMap so the argv1 fallback in resolveLoaderPluginSdkPackageRoot can find the package root.

  2. Provider env vars: Allow external plugins' providerAuthEnvVars from their openclaw.plugin.json manifests to be merged into the env var candidate map at runtime, rather than relying solely on the compile-time generated file.

Environment

  • OpenClaw 2026.3.23-2
  • Node v24.3.0 (nvm)
  • Linux

extent analysis

Fix Plan

To resolve the issue of external plugins failing to load, we need to implement two fixes:

  • Modify the buildPluginLoaderAliasMap function to pass process.argv[1] or import.meta.url to resolveLoaderPluginSdkPackageRoot for proper alias resolution.
  • Update the resolveProviderApiKey function to merge external plugins' providerAuthEnvVars into the env var candidate map at runtime.

Step 1: Modify buildPluginLoaderAliasMap

// In src/plugins/loader.ts
import { buildPluginLoaderAliasMap } from './sdk-alias';

// ...

const aliasMap = buildPluginLoaderAliasMap(modulePath, process.argv[1]);
// In sdk-alias.ts
export function buildPluginLoaderAliasMap(modulePath: string, argv1?: string) {
  // ...
  const packageRoot = resolveLoaderPluginSdkPackageRoot(modulePath, argv1);
  // ...
}

Step 2: Update resolveProviderApiKey

// In src/plugins/loader.ts
import { resolveProviderApiKey } from './sdk-alias';

// ...

const providerApiKey = resolveProviderApiKey(providerId, process.env, pluginManifest);
// In sdk-alias.ts
export function resolveProviderApiKey(providerId: string, env: NodeJS.ProcessEnv, pluginManifest: any) {
  const envVarCandidates = getEnvVarCandidates(pluginManifest);
  // Merge external plugins' providerAuthEnvVars into the env var candidate map
  const mergedEnvVarCandidates = { ...envVarCandidates, ...pluginManifest.providerAuthEnvVars };
  // ...
}

function getEnvVarCandidates(pluginManifest: any) {
  // Load env var candidates from bundled-provider-auth-env-vars.generated.ts
  // and merge with external plugins' providerAuthEnvVars
  const bundledEnvVarCandidates = require('./bundled-provider-auth-env-vars.generated');
  return { ...bundledEnvVarCandidates, ...pluginManifest.providerAuthEnvVars };
}

Verification

To verify the fix, create a new external plugin and install it using openclaw plugins install. Then, inspect the plugin using openclaw plugins inspect to ensure it loads correctly.

Extra Tips

  • Ensure that the openclaw package is properly linked in the plugin's node_modules directory.
  • Verify that the providerAuthEnvVars are correctly defined in the external plugin's openclaw.plugin.json manifest.
  • Test the fix with multiple external plugins to ensure it works consistently.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING