openclaw - 💡(How to fix) Fix [Bug]: 2026.4.27 plugin runtime-deps ENOENT race during hot channel reload leaves outbound permanently broken (regression of #55551) [4 comments, 3 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#74818Fetched 2026-05-01 05:41:00
View on GitHub
Comments
4
Participants
3
Timeline
6
Reactions
2
Author
Timeline (top)
commented ×4closed ×1cross-referenced ×1

On openclaw 2026.4.27 (macOS LaunchAgent gateway), any live edit to `openclaw.json` that triggers a channel hot-reload can fail plugin registration with `ENOENT` on a runtime-deps file that demonstrably exists on disk milliseconds later. When this hits the telegram plugin, it leaves the channel in a permanently cached `Outbound not configured for channel: telegram` state until manual gateway restart — making the bot appear unresponsive while the gateway itself is healthy.

This is functionally a regression of #55551 (closed 2026-04-25, fixed in v2026.3.28 via `resolveOutboundChannelPlugin` registry recovery): the outbound-resolution recovery path covers the missing-plugin case but does not cover the plugin-actively-failed-to-register case, where the cached failure state pre-empts the recovery fallback. Related to #71805 (ENOTEMPTY race on plugin-sdk in 2026.4.24, closed) — same problem area (parallel runtime-deps materialization), different file/error.

Error Message

This is functionally a regression of #55551 (closed 2026-04-25, fixed in v2026.3.28 via `resolveOutboundChannelPlugin` registry recovery): the outbound-resolution recovery path covers the missing-plugin case but does not cover the plugin-actively-failed-to-register case, where the cached failure state pre-empts the recovery fallback. Related to #71805 (ENOTEMPTY race on plugin-sdk in 2026.4.24, closed) — same problem area (parallel runtime-deps materialization), different file/error. Error: ENOENT: no such file or directory, open '/Users/page/.openclaw/plugin-runtime-deps/openclaw-2026.4.27-44803533892a/dist/docs-path-DVJW8JaL.js' 6. From this point, all outbound on telegram fails permanently with the cached error: [ws] ⇄ res ✗ agent errorCode=UNAVAILABLE errorMessage=Error: Outbound not configured for channel: telegram cached=true [warn] Subagent announce give up (retry-limit) … requester=agent:main:telegram:direct:<chatId> retries=3 endedAgo=98s Subagent completion direct announce failed for run …: Error: Outbound not configured for channel: telegram 2. `resolveOutboundChannelPlugin` recovery should cover failed-register state, not just missing-plugin state. When a plugin's last register attempt failed, retry the register before returning the cached `Outbound not configured` error.

  • #71805 (CLOSED 2026-04-26): `ENOTEMPTY` race on plugin-sdk during parallel bootstrap in 2026.4.24. Same problem area (parallel runtime-deps materialization), different error code. The 2026.4.27 release notes mention plugin-runtime-deps caching/file-lock improvements ('reducing repeated startup chunk scans and avoiding FileHandle-GC recovery stalls') — those fixes addressed startup but not the hot-reload path.

Root Cause

For a personal-assistant deployment (Telegram is the primary user-facing channel), this is silent breakage — gateway is healthy, status reports green, but outbound is dead until someone notices and runs kickstart. We hit this twice tonight from different config-change triggers (a cron edit, an agent adding a new bot). It's likely common in any environment with active config evolution.

Fix Action

Workaround

`launchctl kickstart -k gui/$UID/ai.openclaw.gateway` — clean restart with runtime-deps fully present from the start. Telegram plugin then registers successfully and the queued failed deliveries are auto-recovered via `delivery-recovery`. Takes ~10 seconds. Verified working.

RAW_BUFFERClick to expand / collapse

Summary

On openclaw 2026.4.27 (macOS LaunchAgent gateway), any live edit to `openclaw.json` that triggers a channel hot-reload can fail plugin registration with `ENOENT` on a runtime-deps file that demonstrably exists on disk milliseconds later. When this hits the telegram plugin, it leaves the channel in a permanently cached `Outbound not configured for channel: telegram` state until manual gateway restart — making the bot appear unresponsive while the gateway itself is healthy.

This is functionally a regression of #55551 (closed 2026-04-25, fixed in v2026.3.28 via `resolveOutboundChannelPlugin` registry recovery): the outbound-resolution recovery path covers the missing-plugin case but does not cover the plugin-actively-failed-to-register case, where the cached failure state pre-empts the recovery fallback. Related to #71805 (ENOTEMPTY race on plugin-sdk in 2026.4.24, closed) — same problem area (parallel runtime-deps materialization), different file/error.

Environment

  • OpenClaw: 2026.4.27 (cbc2ba0)
  • Node: 25.5.0 (Homebrew)
  • Host: macOS, LaunchAgent gateway under `gui/$UID/ai.openclaw.gateway`
  • Install: `npm i -g openclaw` → `~/.npm-global/lib/node_modules/openclaw`
  • Runtime-deps mirror: `~/.openclaw/plugin-runtime-deps/openclaw-2026.4.27-44803533892a/dist/`

Reproduction

  1. Have an openclaw 2026.4.27 gateway running healthy with telegram channel configured.
  2. Modify `~/.openclaw/openclaw.json` to add or change a telegram account (in our case: an agent added `channels.telegram.accounts.forge` with a SecretRef botToken).
  3. Gateway logs: ``` [reload] config change requires channel reload (telegram) — deferring until N operation(s)... ```
  4. When the deferred reload fires, the telegram plugin re-register may fail with ENOENT on a runtime-deps file: ``` [plugins] telegram failed during register from /…/openclaw/dist/extensions/telegram/index.js: Error: ENOENT: no such file or directory, open '/Users/page/.openclaw/plugin-runtime-deps/openclaw-2026.4.27-44803533892a/dist/docs-path-DVJW8JaL.js' [plugins] 1 plugin(s) failed to initialize (register: telegram). Run 'openclaw plugins list' for details. ```
  5. The file exists on disk (verified with `ls -la` immediately after — same path, present). The race is in the loader's `fs.open()` returning ENOENT during the plugin-runtime-deps mirror materialization window.
  6. From this point, all outbound on telegram fails permanently with the cached error: ``` [ws] ⇄ res ✗ agent errorCode=UNAVAILABLE errorMessage=Error: Outbound not configured for channel: telegram cached=true [warn] Subagent announce give up (retry-limit) … requester=agent:main:telegram:direct:<chatId> retries=3 endedAgo=98s Subagent completion direct announce failed for run …: Error: Outbound not configured for channel: telegram ```
  7. The gateway's `resolveOutboundChannelPlugin` recovery path (added per #55551 in v2026.3.28) does NOT recover here — likely because the plugin is in a 'failed-register' state, not a 'missing' state.

Workaround

`launchctl kickstart -k gui/$UID/ai.openclaw.gateway` — clean restart with runtime-deps fully present from the start. Telegram plugin then registers successfully and the queued failed deliveries are auto-recovered via `delivery-recovery`. Takes ~10 seconds. Verified working.

Hypothesized fix areas

  1. Runtime-deps materialization should be idempotent and complete-before-import. During channel hot-reload, the loader should wait for the runtime-deps mirror to be coherent before attempting plugin re-register. Or use a manifest-driven file existence pre-check before `require()`.
  2. `resolveOutboundChannelPlugin` recovery should cover failed-register state, not just missing-plugin state. When a plugin's last register attempt failed, retry the register before returning the cached `Outbound not configured` error.
  3. Cached registration failures should have a TTL (current behavior appears to cache forever within the gateway lifetime). A small TTL with bounded retry would let transient races self-heal.

Why this matters

For a personal-assistant deployment (Telegram is the primary user-facing channel), this is silent breakage — gateway is healthy, status reports green, but outbound is dead until someone notices and runs kickstart. We hit this twice tonight from different config-change triggers (a cron edit, an agent adding a new bot). It's likely common in any environment with active config evolution.

Cross-references

  • #55551 (CLOSED 2026-04-25, v2026.3.28): fixed the `Outbound not configured for channel: telegram` symptom for the missing-plugin case. This bug is a regression of that — same symptom, different root cause path (failed-register vs missing).
  • #71805 (CLOSED 2026-04-26): `ENOTEMPTY` race on plugin-sdk during parallel bootstrap in 2026.4.24. Same problem area (parallel runtime-deps materialization), different error code. The 2026.4.27 release notes mention plugin-runtime-deps caching/file-lock improvements ('reducing repeated startup chunk scans and avoiding FileHandle-GC recovery stalls') — those fixes addressed startup but not the hot-reload path.
  • #55099 (OPEN): unrelated but in the telegram/network area — the 'sticky IPv4-only dispatcher' isn't actually sticky across requests. Mentioning only because the symptoms can compound during the same incident.

extent analysis

TL;DR

The most likely fix involves modifying the resolveOutboundChannelPlugin recovery path to cover the 'failed-register' state and implementing a TTL for cached registration failures to prevent permanent caching of errors.

Guidance

  1. Review the resolveOutboundChannelPlugin function: Ensure it can handle the 'failed-register' state by retrying the plugin registration before returning the cached error.
  2. Implement a TTL for cached registration failures: Introduce a time-to-live (TTL) mechanism for cached registration failures to allow for transient errors to self-heal.
  3. Investigate idempotent runtime-deps materialization: Consider making runtime-deps materialization idempotent and complete before attempting plugin re-registration to prevent races.
  4. Verify the workaround: Use the provided launchctl kickstart command as a temporary workaround to restart the gateway and recover from the failed state.
  5. Monitor for similar issues: Keep an eye on related issues, such as #55099, as their symptoms can compound during the same incident.

Example

No specific code example is provided due to the complexity of the issue and the need for a thorough review of the resolveOutboundChannelPlugin function and runtime-deps materialization process.

Notes

The provided workaround using launchctl kickstart can temporarily resolve the issue, but a permanent fix requires addressing the root cause. The resolveOutboundChannelPlugin recovery path and TTL for cached registration failures are crucial in preventing similar issues.

Recommendation

Apply the workaround using launchctl kickstart until a permanent fix is implemented, as it provides a reliable way to recover from the failed state.

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 [Bug]: 2026.4.27 plugin runtime-deps ENOENT race during hot channel reload leaves outbound permanently broken (regression of #55551) [4 comments, 3 participants]