openclaw - 💡(How to fix) Fix Plugin update writes trigger false gateway restarts via runtime-config paths (firecrawl webFetch + gateway.auth.token) [3 comments, 1 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#68732Fetched 2026-04-19 15:08:11
View on GitHub
Comments
3
Participants
1
Timeline
3
Reactions
0
Participants
Timeline (top)
commented ×3

updatePluginsAfterCoreUpdate() uses params.configSnapshot.config (runtime config) as the base config for plugin update persistence. When that runtime config includes normalized/materialized fields like plugins.entries.firecrawl.config.webFetch, the later replaceConfigFile() writes them back into ~/.openclaw/openclaw.json.

That creates a restart loop on systems where plugin update runs while the gateway is live:

  1. plugin update rewrites config
  2. rewritten config now includes plugins.entries.firecrawl.config.webFetch
  3. gateway reload logic detects plugins.entries.firecrawl.config.webFetch
  4. gateway restarts
  5. next plugin update can repeat the same write

Root Cause

updatePluginsAfterCoreUpdate() uses params.configSnapshot.config (runtime config) as the base config for plugin update persistence. When that runtime config includes normalized/materialized fields like plugins.entries.firecrawl.config.webFetch, the later replaceConfigFile() writes them back into ~/.openclaw/openclaw.json.

That creates a restart loop on systems where plugin update runs while the gateway is live:

  1. plugin update rewrites config
  2. rewritten config now includes plugins.entries.firecrawl.config.webFetch
  3. gateway reload logic detects plugins.entries.firecrawl.config.webFetch
  4. gateway restarts
  5. next plugin update can repeat the same write

Fix Action

Fix / Workaround

Validation after patching candidate 1:

  • file diff only showed meta.lastTouchedAt and plugins.installs.lossless-claw.{resolvedAt,installedAt}
  • but gateway still logged:
    • config change detected; evaluating reload (meta.lastTouchedAt, gateway.auth.token, plugins.entries.firecrawl.config.webFetch, plugins.installs.lossless-claw.resolvedAt, plugins.installs.lossless-claw.installedAt)
    • config change requires gateway restart (gateway.auth.token, plugins.entries.firecrawl.config.webFetch, plugins.installs.lossless-claw.resolvedAt, plugins.installs.lossless-claw.installedAt)

That suggests a second bug in runtime snapshot projection / write notification. Relevant code:

if (hadBothSnapshots) nextCfg = coerceConfig(applyMergePatch(runtimeConfigSourceSnapshot, createMergePatch(runtimeConfigSnapshot, cfg)));

inside io-5pxHCi7V.js writeConfigFile().

Local stopgaps applied on this machine

Patched local files:

  • /opt/homebrew/lib/node_modules/openclaw/dist/update-cli-D3DsAjPr.js
  • /opt/homebrew/lib/node_modules/openclaw/dist/io-5pxHCi7V.js

Code Example

const syncResult = await syncPluginsForUpdateChannel({
  config: params.configSnapshot.config,
  channel: params.channel,
  workspaceDir: params.root,
  logger: pluginLogger
});

---

return {
  sourceConfig,
  resolved: sourceConfig,
  runtimeConfig,
  config: runtimeConfig,
}

---

const pluginUpdateBaseConfig = params.configSnapshot.sourceConfig ?? params.configSnapshot.config;
const syncResult = await syncPluginsForUpdateChannel({
  config: pluginUpdateBaseConfig,
  channel: params.channel,
  workspaceDir: params.root,
  logger: pluginLogger
});

---

if (hadBothSnapshots) nextCfg = coerceConfig(applyMergePatch(runtimeConfigSourceSnapshot, createMergePatch(runtimeConfigSnapshot, cfg)));

---

projectConfigOntoRuntimeSourceSnapshot(cfg)
RAW_BUFFERClick to expand / collapse

OpenClaw core bug: plugin update persists runtime-only plugins.entries.firecrawl.config.webFetch into source config

Summary

updatePluginsAfterCoreUpdate() uses params.configSnapshot.config (runtime config) as the base config for plugin update persistence. When that runtime config includes normalized/materialized fields like plugins.entries.firecrawl.config.webFetch, the later replaceConfigFile() writes them back into ~/.openclaw/openclaw.json.

That creates a restart loop on systems where plugin update runs while the gateway is live:

  1. plugin update rewrites config
  2. rewritten config now includes plugins.entries.firecrawl.config.webFetch
  3. gateway reload logic detects plugins.entries.firecrawl.config.webFetch
  4. gateway restarts
  5. next plugin update can repeat the same write

Evidence

Historical runtime log

From /tmp/openclaw/openclaw-2026-04-18.log:

  • Updating plugins...
  • Config overwrite: /Users/faisalshomemacmini/.openclaw/openclaw.json ... changedPaths=2
  • immediately followed by:
    • config change detected; evaluating reload (meta.lastTouchedVersion, meta.lastTouchedAt, plugins.entries.firecrawl.config.webFetch, plugins.installs.lossless-claw.resolvedAt, plugins.installs.lossless-claw.installedAt)
    • config change requires gateway restart (plugins.entries.firecrawl.config.webFetch, plugins.installs.lossless-claw.resolvedAt, plugins.installs.lossless-claw.installedAt) ...

Separate gateway log evidence

From ~/.openclaw/logs/gateway.log:

  • [exec] elevated command openclaw plugins update --all 2>&1
  • later:
    • [reload] config change detected; evaluating reload (meta.lastTouchedAt, plugins.entries.firecrawl.config.webFetch)
    • gateway receives SIGTERM and restarts

Code path

/opt/homebrew/lib/node_modules/openclaw/dist/update-cli-D3DsAjPr.js

const syncResult = await syncPluginsForUpdateChannel({
  config: params.configSnapshot.config,
  channel: params.channel,
  workspaceDir: params.root,
  logger: pluginLogger
});

params.configSnapshot.config is runtime config, not source config.

/opt/homebrew/lib/node_modules/openclaw/dist/io-5pxHCi7V.js

return {
  sourceConfig,
  resolved: sourceConfig,
  runtimeConfig,
  config: runtimeConfig,
}

Then plugin update persists via:

  • replaceConfigFile(...)
  • which writes the supplied config back to the source file.

Expected behavior

Plugin update persistence should operate on the source config snapshot, not runtime config.

Minimal fix candidates

Candidate 1: plugin update should use source config

Use params.configSnapshot.sourceConfig ?? params.configSnapshot.config in updatePluginsAfterCoreUpdate().

Example:

const pluginUpdateBaseConfig = params.configSnapshot.sourceConfig ?? params.configSnapshot.config;
const syncResult = await syncPluginsForUpdateChannel({
  config: pluginUpdateBaseConfig,
  channel: params.channel,
  workspaceDir: params.root,
  logger: pluginLogger
});

Candidate 2: writeConfigFile() should project source config correctly before notifying runtime listeners

In practice, candidate 1 alone was not sufficient on Fas's Mac.

Validation after patching candidate 1:

  • file diff only showed meta.lastTouchedAt and plugins.installs.lossless-claw.{resolvedAt,installedAt}
  • but gateway still logged:
    • config change detected; evaluating reload (meta.lastTouchedAt, gateway.auth.token, plugins.entries.firecrawl.config.webFetch, plugins.installs.lossless-claw.resolvedAt, plugins.installs.lossless-claw.installedAt)
    • config change requires gateway restart (gateway.auth.token, plugins.entries.firecrawl.config.webFetch, plugins.installs.lossless-claw.resolvedAt, plugins.installs.lossless-claw.installedAt)

That suggests a second bug in runtime snapshot projection / write notification. Relevant code:

if (hadBothSnapshots) nextCfg = coerceConfig(applyMergePatch(runtimeConfigSourceSnapshot, createMergePatch(runtimeConfigSnapshot, cfg)));

inside io-5pxHCi7V.js writeConfigFile().

A more correct projection path already exists nearby:

projectConfigOntoRuntimeSourceSnapshot(cfg)

So the deeper fix likely needs both:

  1. plugin update paths should start from sourceConfig
  2. runtime snapshot projection during writes should not re-introduce runtime-only fields into reload diffs / notifications

Local stopgaps applied on this machine

Patched local files:

  • /opt/homebrew/lib/node_modules/openclaw/dist/update-cli-D3DsAjPr.js
  • /opt/homebrew/lib/node_modules/openclaw/dist/io-5pxHCi7V.js

Backups created:

  • /opt/homebrew/lib/node_modules/openclaw/dist/update-cli-D3DsAjPr.js.pre-sourceconfig-fix-20260418-184210.bak
  • /opt/homebrew/lib/node_modules/openclaw/dist/io-5pxHCi7V.js.pre-runtime-projection-fix-20260418-185008.bak

Validation status on this machine:

  • candidate 1 reduced on-disk diff to meta/install timestamps only
  • but gateway reload still reported gateway.auth.token and plugins.entries.firecrawl.config.webFetch
  • candidate 2 was also patched locally for further testing
  • as of the latest validation, plugin update still triggered a restart-required reload, so upstream investigation is still needed

Notes

A local patch will be overwritten by future OpenClaw upgrades, so this needs an upstream fix in the distributed package.

extent analysis

TL;DR

The most likely fix involves modifying the updatePluginsAfterCoreUpdate() function to use the sourceConfig instead of runtimeConfig for plugin update persistence, and ensuring that runtime snapshot projection during writes does not reintroduce runtime-only fields.

Guidance

  • Identify the updatePluginsAfterCoreUpdate() function and modify it to use params.configSnapshot.sourceConfig instead of params.configSnapshot.config for plugin update persistence.
  • Review the writeConfigFile() function to ensure that it correctly projects the source config onto the runtime snapshot without reintroducing runtime-only fields.
  • Validate the changes by checking the config file diff and gateway reload logs to ensure that the restart loop is resolved.
  • Consider creating a backup of the original files before applying any patches to ensure that changes can be easily reverted if needed.

Example

const pluginUpdateBaseConfig = params.configSnapshot.sourceConfig ?? params.configSnapshot.config;
const syncResult = await syncPluginsForUpdateChannel({
  config: pluginUpdateBaseConfig,
  channel: params.channel,
  workspaceDir: params.root,
  logger: pluginLogger
});

Notes

The provided local patches may be overwritten by future OpenClaw upgrades, so an upstream fix in the distributed package is necessary for a permanent solution. Additionally, the projectConfigOntoRuntimeSourceSnapshot() function may need to be used instead of coerceConfig() and applyMergePatch() to correctly project the source config onto the runtime snapshot.

Recommendation

Apply the workaround by modifying the updatePluginsAfterCoreUpdate() function to use sourceConfig and ensuring correct runtime snapshot projection, as this is the most direct way to resolve the restart loop issue. However, it is crucial to wait for an official upstream fix to ensure a permanent solution that will not be overwritten by future updates.

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…

FAQ

Expected behavior

Plugin update persistence should operate on the source config snapshot, not runtime config.

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 update writes trigger false gateway restarts via runtime-config paths (firecrawl webFetch + gateway.auth.token) [3 comments, 1 participants]