openclaw - 💡(How to fix) Fix Plugin loader: silent failures on legacy/invalid plugin contracts cost hours of debugging [2 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#78301Fetched 2026-05-07 03:38:32
View on GitHub
Comments
2
Participants
2
Timeline
3
Reactions
2
Author
Timeline (top)
commented ×2cross-referenced ×1

Error Message

Suggested improvement: in fetchWithRuntimeDispatcher / fetchWithSsrFGuard (or wherever plugin requests funnel through), strip and warn on plugin-supplied Content-Length with a one-time deprecation log per plugin:

Root Cause

Repro (real): the Tencent plugin's dist/src/api/api.js line 164 manually sets Content-Length. Every API call (getUpdates, login, send) fails synchronously. Surface symptom is Failed to start login: TypeError: fetch failed and a Weixin runtime initialization timeout loop. The actual root cause took bisecting through the plugin's import graph to find — the openclaw SDK import was what flipped fetch behavior.

Fix Action

Fix / Workaround

Case 1 — Strict undici dispatcher rejects manual Content-Length, but the cause is buried

OpenClaw installs a hardened global undici dispatcher (visible via ensureGlobalUndiciEnvProxyDispatcher / fetchWithSsrFGuard in openclaw/plugin-sdk/infra-runtime) that correctly rejects plugin-supplied Content-Length headers with UND_ERR_INVALID_ARG: invalid content-length header. This is good — manual Content-Length enables request-smuggling vectors when the value drifts from the actual transmitted bytes.

Suggested improvement: in fetchWithRuntimeDispatcher / fetchWithSsrFGuard (or wherever plugin requests funnel through), strip and warn on plugin-supplied Content-Length with a one-time deprecation log per plugin:

Code Example

[plugins] [openclaw-weixin] dropping plugin-supplied Content-Length header on POST /ilink/bot/get_bot_qrcode — undici computes this itself. Manual values risk request smuggling. See <docs link>.

---

"runtime": { "specifier": "./runtime-api.js", "exportName": "setWhatsAppRuntime" }

---

[plugins] [openclaw-weixin] no `runtime` declaration in openclaw.plugin.json; runtime injection skipped. Plugins using register({api: {runtime}}) must migrate to manifest-declared runtime. See <migration doc>.
RAW_BUFFERClick to expand / collapse

While debugging why @tencent-weixin/[email protected] was failing on a 2026.5.4 host, I hit two cases where the plugin loader silently tolerates plugin-authoring bugs that surface much later as opaque runtime errors. Both could have been caught at plugin load time with a clear message naming the plugin and the contract violated.

This is a request for better diagnostics — not for relaxing the underlying defenses, which are correct.

Case 1 — Strict undici dispatcher rejects manual Content-Length, but the cause is buried

OpenClaw installs a hardened global undici dispatcher (visible via ensureGlobalUndiciEnvProxyDispatcher / fetchWithSsrFGuard in openclaw/plugin-sdk/infra-runtime) that correctly rejects plugin-supplied Content-Length headers with UND_ERR_INVALID_ARG: invalid content-length header. This is good — manual Content-Length enables request-smuggling vectors when the value drifts from the actual transmitted bytes.

Problem: the rejection bubbles up as a generic TypeError: fetch failed. The actual cause is only visible if the plugin inspects err.cause. Most plugin code does String(err) and the signal is lost. End result for the operator: a silent restart loop with no actionable log line.

Repro (real): the Tencent plugin's dist/src/api/api.js line 164 manually sets Content-Length. Every API call (getUpdates, login, send) fails synchronously. Surface symptom is Failed to start login: TypeError: fetch failed and a Weixin runtime initialization timeout loop. The actual root cause took bisecting through the plugin's import graph to find — the openclaw SDK import was what flipped fetch behavior.

Suggested improvement: in fetchWithRuntimeDispatcher / fetchWithSsrFGuard (or wherever plugin requests funnel through), strip and warn on plugin-supplied Content-Length with a one-time deprecation log per plugin:

[plugins] [openclaw-weixin] dropping plugin-supplied Content-Length header on POST /ilink/bot/get_bot_qrcode — undici computes this itself. Manual values risk request smuggling. See <docs link>.

This converts a hard failure into a soft signal that names the offending plugin.

Case 2 — Plugins without "runtime" in manifest get no runtime injection and no warning

@openclaw/[email protected] declares in its plugin manifest:

"runtime": { "specifier": "./runtime-api.js", "exportName": "setWhatsAppRuntime" }

…and the host correctly calls setWhatsAppRuntime on load.

@tencent-weixin/[email protected] does not declare runtime in its openclaw.plugin.json or package.json#openclaw. Its register(api) reads api.runtime (the legacy contract) and only calls setWeixinRuntime(api.runtime) when truthy. On 2026.5.4 it isn't, so pluginRuntime stays null, waitForWeixinRuntime times out at 10s, and the channel enters a permanent restart loop with Weixin runtime initialization timeout — logged by the plugin, not the host.

The loader is silent about this mismatch. There's no host-side diagnostic at plugin-load time that says this plugin uses the legacy runtime contract; runtime injection skipped.

Suggested improvement: after calling register(api), if the plugin's manifest doesn't declare runtime AND the host didn't pass runtime into api, emit a one-time deprecation warning per plugin:

[plugins] [openclaw-weixin] no `runtime` declaration in openclaw.plugin.json; runtime injection skipped. Plugins using register({api: {runtime}}) must migrate to manifest-declared runtime. See <migration doc>.

Bonus — Migration guide

A Plugin author's migration guide: 2026.4.x → 2026.5.x linked from the relevant sections of plugin-sdk would close the loop. The Tencent plugin (Tencent/openclaw-weixin on GitHub) hasn't migrated, possibly because the migration was never visible to them. A short section showing the before/after for runtime: {specifier, exportName} would help every external plugin author.

Environment

  • OpenClaw: 2026.5.4
  • Node: 22.22.2
  • OS: Ubuntu 24.04 arm64 (Raspberry Pi 5)
  • Plugin context: @tencent-weixin/[email protected] (will file the actual plugin bugs at Tencent/openclaw-weixin separately)

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

openclaw - 💡(How to fix) Fix Plugin loader: silent failures on legacy/invalid plugin contracts cost hours of debugging [2 comments, 2 participants]