openclaw - 💡(How to fix) Fix plugins install fails to commit install records when plugins config is loaded via $include [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#77931Fetched 2026-05-06 06:19:13
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
2
Timeline (top)
closed ×1commented ×1

openclaw plugins install fails to persist install records when the plugins config section is loaded via $include. The plugin files install correctly to disk, but the commit step is rejected by the $include-ownership protection added in cd0092c654 (fix for #70087). This leaves the plugin physically present but unloadable at runtime.

Error Message

[openclaw] Failed to start CLI: Error: Config write would flatten $include-owned config at plugins; edit that include file directly or remove the $include first. at preserveUntouchedIncludes (.../io-D-sSCO_A.js:1324:58) at resolvePersistCandidateForWrite (.../io-D-sSCO_A.js:1333:20) at writeConfigFile (.../io-D-sSCO_A.js:19239:23) ... commitPluginInstallRecordsWithWriter ... persistPluginInstall ... tryInstallPluginOrHookPackFromNpmSpec

Root Cause

  • Adding installs: {} to plugins.json5 then re-running install: Still errors with the same $include flatten message.
  • Inlining plugins back into openclaw.json: Install proceeds further but then errors with Config validation failed: agents: Unrecognized key: "$include" because the writer revalidates the full file and other sections are still $include'd.
  • openclaw config patch for plugins.installs.brave: Rejected with plugins.installs is managed by the plugin index and cannot be edited with config set.

Fix Action

Fix / Workaround

Workarounds Tried (None Work)

  • Adding installs: {} to plugins.json5 then re-running install: Still errors with the same $include flatten message.
  • Inlining plugins back into openclaw.json: Install proceeds further but then errors with Config validation failed: agents: Unrecognized key: "$include" because the writer revalidates the full file and other sections are still $include'd.
  • openclaw config patch for plugins.installs.brave: Rejected with plugins.installs is managed by the plugin index and cannot be edited with config set.

Code Example

{ "plugins": { "$include": "./plugins.json5" } }

---

openclaw plugins install @openclaw/brave-plugin --force

---

[openclaw] Failed to start CLI: Error: Config write would flatten $include-owned config at plugins; edit that include file directly or remove the $include first.
       at preserveUntouchedIncludes (.../io-D-sSCO_A.js:1324:58)
       at resolvePersistCandidateForWrite (.../io-D-sSCO_A.js:1333:20)
       at writeConfigFile (.../io-D-sSCO_A.js:19239:23)
       ... commitPluginInstallRecordsWithWriter ... persistPluginInstall ... tryInstallPluginOrHookPackFromNpmSpec

---

[plugins] installed plugin package requires compiled runtime output for TypeScript entry index.ts: expected ./dist/index.js, ./dist/index.mjs, ./dist/index.cjs, index.js, index.mjs, index.cjs (plugin=brave, source=/home/.../@openclaw/brave-plugin)
RAW_BUFFERClick to expand / collapse

Summary

openclaw plugins install fails to persist install records when the plugins config section is loaded via $include. The plugin files install correctly to disk, but the commit step is rejected by the $include-ownership protection added in cd0092c654 (fix for #70087). This leaves the plugin physically present but unloadable at runtime.

Steps to Reproduce

  1. Use $include for the plugins section in openclaw.json:

    { "plugins": { "$include": "./plugins.json5" } }

    where plugins.json5 contains allow, slots, entries (no installs).

  2. Run:

    openclaw plugins install @openclaw/brave-plugin --force
  3. Files install correctly to ~/.openclaw/npm/node_modules/@openclaw/brave-plugin/ (including dist/index.js).

  4. The persist step fails with:

    [openclaw] Failed to start CLI: Error: Config write would flatten $include-owned config at plugins; edit that include file directly or remove the $include first.
        at preserveUntouchedIncludes (.../io-D-sSCO_A.js:1324:58)
        at resolvePersistCandidateForWrite (.../io-D-sSCO_A.js:1333:20)
        at writeConfigFile (.../io-D-sSCO_A.js:19239:23)
        ... commitPluginInstallRecordsWithWriter ... persistPluginInstall ... tryInstallPluginOrHookPackFromNpmSpec
  5. plugins.installs.<id> is never written. On gateway restart:

    [plugins] installed plugin package requires compiled runtime output for TypeScript entry index.ts: expected ./dist/index.js, ./dist/index.mjs, ./dist/index.cjs, index.js, index.mjs, index.cjs (plugin=brave, source=/home/.../@openclaw/brave-plugin)

    even though dist/index.js exists and loads fine via node -e "import(...)".

  6. Plugin is silently skipped at runtime. For Brave, web_search falls back to Gemini despite tools.web.search.provider: "brave" and a valid BRAVE_API_KEY.

Expected Behavior

openclaw plugins install should write the install record into the include target file when the section is $include-owned (the same way the existing protection assumes for read), or persist install records to a separate index file outside the user-edited config.

Workarounds Tried (None Work)

  • Adding installs: {} to plugins.json5 then re-running install: Still errors with the same $include flatten message.
  • Inlining plugins back into openclaw.json: Install proceeds further but then errors with Config validation failed: agents: Unrecognized key: "$include" because the writer revalidates the full file and other sections are still $include'd.
  • openclaw config patch for plugins.installs.brave: Rejected with plugins.installs is managed by the plugin index and cannot be edited with config set.

Users with any $include layout for plugins are effectively unable to install new external plugins.

Environment

  • OpenClaw v2026.5.4 (commit 325df3e), pnpm install
  • Linux 6.17.0-23-generic x64, Node 22.22.2
  • Gateway running as systemd user service

Impact

  • Web search silently degrades to fallback provider with no warning to the agent (just provider: "gemini" in the response). Users may not notice.
  • Affects any externally-installed plugin (brave, discord, etc.) on configs that use $include for plugins — which is the recommended layout for survive-updates user config.

Suggested Fix Area

commitPluginInstallRecordsWithWriterreplaceConfigFile path. Either:

  1. Detect $include ownership for the plugins path and append the install record to the included file directly, or
  2. Move install records out of plugins.installs into a dedicated state file (e.g. ~/.openclaw/plugins-installs.json) that doesn't participate in config write protection.

Related

  • #70087 — the original $include flatten bug whose fix introduced the protection that now blocks this path

extent analysis

TL;DR

The most likely fix involves modifying the commitPluginInstallRecordsWithWriter function to handle $include-owned config sections by either appending install records to the included file or moving them to a separate state file.

Guidance

  • Identify the commitPluginInstallRecordsWithWriter function and its interaction with the config writer to understand how install records are persisted.
  • Consider implementing a check for $include ownership in the plugins section to determine the correct persistence path.
  • Evaluate the trade-offs between appending install records to the included file versus moving them to a dedicated state file, considering factors like config write protection and user experience.
  • Review the replaceConfigFile path to ensure it correctly handles the updated persistence logic.

Example

// Pseudocode example of checking $include ownership and appending install records
if (isIncludeOwned(config, 'plugins')) {
  const includeFile = getIncludeFile(config, 'plugins');
  appendInstallRecord(includeFile, installRecord);
} else {
  // Existing persistence logic
}

Notes

The suggested fix area is focused on the commitPluginInstallRecordsWithWriter function, but the actual implementation may require changes to other parts of the codebase. Additionally, the choice between appending to the included file or moving to a separate state file will depend on the specific requirements and constraints of the OpenClaw configuration system.

Recommendation

Apply a workaround by modifying the commitPluginInstallRecordsWithWriter function to handle $include-owned config sections, as this will allow users to install external plugins while a more permanent solution is developed.

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