openclaw - ✅(Solved) Fix [Bug]: `openclaw status` fails on file-backed SecretRef configs because route/plugin loading re-reads unresolved raw config [2 pull requests, 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#57272Fetched 2026-04-08 01:51:45
View on GitHub
Comments
0
Participants
1
Timeline
0
Reactions
0
Participants

When a channel secret has been migrated out of ~/.openclaw/openclaw.json into a file-backed SecretRef, openclaw status can fail before the command reaches its own SecretRef resolution path.

In my case, channels.feishu.appSecret was migrated to:

{
  "source": "file",
  "provider": "localfile",
  "id": "/channels/feishu/appSecret"
}

The active gateway runtime is healthy and the secret resolves correctly there, but the local CLI status path crashes while loading plugins.

Error Message

[plugins] feishu failed during register ... Error: channels.feishu.appSecret: unresolved SecretRef "file:localfile:/channels/feishu/appSecret". Resolve this command against an active gateway runtime snapshot before reading it. [openclaw] Failed to start CLI: PluginLoadFailureError: plugin load failed: feishu ...

Root Cause

There are two linked problems:

  1. src/cli/program/routes.ts

    • The route-first CLI path preloads plugins for plain status before the status command runs.
    • That means plugin registration happens before status has a chance to resolve SecretRefs via gateway.
  2. src/cli/plugin-registry.ts

    • ensurePluginRegistryLoaded() always calls loadConfig().
    • This discards the already-resolved config that status later computes via resolveCommandSecretRefsViaGateway(...).

The result is:

  • status has a valid secret-resolution path
  • but plugin loading bypasses it and re-opens the unresolved raw config

Fix Action

Fix / Workaround

Minimal Patch

PR fix notes

PR #65378: fix(slack): forward resolved token in all action call sites

Description (problem / solution / changelog)

In the default single-account SecretRef deployment, buildActionOpts() returned undefined — causing 12+ downstream action sites (sendMessage, editMessage, readMessages, react, pin/unpin, uploadFile, memberInfo, emojiList) to call resolveToken()loadConfig() which crashes on SecretRef objects.

#62097 fixed this for downloadFile only with a site-specific workaround. This completes the pattern structurally: buildActionOpts now always returns an object with the resolved token, eliminating the undefined return path entirely. The site-specific downloadFile workaround is removed (now redundant).

Multi-account and token-override behavior is preserved — accountId and custom tokens are still forwarded when configured.

Refs #57272, #63937 Follow-up to #62097

Changed files

  • extensions/slack/src/action-runtime.test.ts (modified, +89/-36)
  • extensions/slack/src/action-runtime.ts (modified, +1/-7)

PR #66818: fix(secrets): align SecretRef inspect/strict behavior across preload/runtime paths

Description (problem / solution / changelog)

Summary

  • Problem: SecretRef handling was inconsistent across plugin preload, registration/setup surfaces, and runtime auth/tool paths. Some read-only flows could fail early on unresolved refs.
  • Why it matters: non-runtime commands and status paths should stay resilient when config contains unresolved SecretRefs, while runtime should remain strict where credentials must actually resolve.
  • What changed:
    • Added a shared inspect/strict SecretRef resolution contract in src/config/types.secrets.ts, exported via src/plugin-sdk/secret-input.ts.
    • Routed status deferred plugin preload through explicit resolved/source snapshots (src/cli/plugin-registry-loader.ts, src/commands/status.scan.fast-json.ts).
    • Updated read-only provider status resolution to prefer inspectAccount and skip throwing accounts (src/commands/agents.providers.ts).
    • Removed credential resolution from Slack route registration (extensions/slack/src/http/plugin-routes.ts).
    • Normalized provider/tool SecretRef handling for custom provider auth, xAI tool auth, and Firecrawl config paths.
    • Added Exa web-search SecretRef target coverage and synced docs matrix/surface.
    • Updated Telegram runtime token resolution to support env SecretRefs while preserving strict behavior for unresolved non-env refs.
  • What did NOT change (scope boundary):
    • No unrelated channel/runtime behavior refactors outside SecretRef lifecycle and snapshot-threading surfaces.
    • No protocol or command-surface expansion.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #63937
  • Closes #57272
  • Closes #57684
  • Closes #64955
  • Closes #65510
  • Supersedes #49805
  • Supersedes #65833
  • Supersedes #56784
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: SecretRef reads were happening at multiple lifecycle points without one shared contract for read-only inspection vs strict runtime resolution.
  • Missing detection / guardrail: registration/read-only flows were not consistently guarded against unresolved refs, and status preload did not always reuse explicit resolved/source snapshots.
  • Contributing context (if known): plugin/channel/provider codepaths evolved with local helpers, causing divergence in when unresolved refs throw vs degrade gracefully.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
    • src/config/types.secrets.resolution.test.ts
    • src/cli/plugin-registry-loader.test.ts
    • src/commands/status.scan.test.ts
    • src/commands/agents.providers.test.ts
    • extensions/slack/src/http/plugin-routes.test.ts
    • src/agents/model-auth.test.ts
    • extensions/xai/src/tool-auth-shared.test.ts
    • extensions/firecrawl/src/firecrawl-tools.test.ts
    • src/secrets/target-registry.test.ts
    • src/cli/command-secret-targets.test.ts
    • extensions/telegram/src/token.test.ts
  • Scenario the test should lock in:
    • Read-only paths do not hard-fail on unresolved SecretRefs.
    • Runtime paths remain strict where non-env refs are unresolved.
    • Deferred plugin preload receives explicit resolved/source snapshots.
    • Secret target registry/docs stay in sync.
  • Why this is the smallest reliable guardrail:
    • These seams are exactly where resolution mode and snapshot threading decisions are made.
  • Existing test that already covers this (if any):
    • Existing status/agents/provider/channel tests were extended at those boundaries.
  • If no new test is added, why not:
    • N/A

User-visible / Behavior Changes

  • openclaw agents list and other read-only/status paths are more resilient when unresolved SecretRefs exist in channel/provider configs.
  • Slack route registration no longer resolves credential refs at registration time.
  • Status deferred plugin preload uses explicit resolved/source config snapshots.
  • Custom provider/xAI/Firecrawl SecretRef handling now treats env refs and unresolved refs consistently for runtime-safe behavior.
  • Exa web-search SecretRef target is now included in secret target registry/docs.
  • Telegram runtime can resolve env-backed SecretRefs for bot tokens while retaining strict handling for unresolved non-env refs.

Diagram (if applicable)

Before:
[CLI/read-only preload] -> [strict secret read in mixed paths] -> [throws early on unresolved ref]

After:
[CLI/read-only preload] -> [inspect mode + resolved/source snapshot threading] -> [degrades safely]
[runtime send/start] -> [strict resolution at execution boundary] -> [throws only when truly required]

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (Yes)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:
    • Risk: accidentally making runtime secret resolution too permissive.
    • Mitigation: strict mode is preserved for runtime-required non-env refs; inspect mode is explicitly scoped to read-only/setup/preload surfaces.

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22+, pnpm workspace
  • Model/provider: N/A
  • Integration/channel (if any): Slack, Telegram, xAI, Firecrawl, model providers
  • Relevant config (redacted): configs containing structured SecretRefs (env + non-env)

Steps

  1. Add unresolved SecretRef values to affected config paths.
  2. Run read-only/status commands and plugin preload flows.
  3. Run runtime credential paths for affected providers/channels.

Expected

  • Read-only/status flows do not crash on unresolved refs.
  • Runtime env refs resolve from environment where applicable.
  • Runtime unresolved non-env refs remain strict failures.

Actual

  • Matches expected in updated tests and targeted command-path verification.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios:
    • Built project successfully (pnpm build).
    • Verified Plugin SDK API drift workflow (pnpm plugin-sdk:api:check, pnpm plugin-sdk:api:gen, re-check pass).
    • Ran targeted tests covering config resolver, status preload, provider auth/tool paths, Slack route registration, Exa target/docs sync, and Telegram token resolution.
  • Edge cases checked:
    • Env SecretRef availability vs missing env var.
    • Unresolved non-env SecretRef behavior in runtime paths.
    • Read-only surfaces with inspect-first handling.
  • What you did not verify:
    • Full repository pnpm test did not finish green due unrelated failing shards outside this PR surface.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:
    • N/A

Risks and Mitigations

  • Risk:

    • Inspect-mode use could expand unintentionally into runtime paths.
    • Mitigation:
      • Strict mode remains default for runtime helper paths; tests assert strict behavior for unresolved non-env refs.
  • Risk:

    • Secret target registry changes can drift from docs.
    • Mitigation:
      • Updated matrix + credential surface docs and docs-sync tests.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • docs/.generated/plugin-sdk-api-baseline.sha256 (modified, +2/-2)
  • docs/reference/secretref-credential-surface.md (modified, +1/-0)
  • docs/reference/secretref-user-supplied-credentials-matrix.json (modified, +7/-0)
  • extensions/firecrawl/src/config.ts (modified, +92/-25)
  • extensions/firecrawl/src/firecrawl-tools.test.ts (modified, +131/-0)
  • extensions/slack/src/http/plugin-routes.test.ts (added, +62/-0)
  • extensions/slack/src/http/plugin-routes.ts (modified, +4/-3)
  • extensions/telegram/src/token.test.ts (modified, +144/-1)
  • extensions/telegram/src/token.ts (modified, +96/-7)
  • extensions/xai/src/tool-auth-shared.test.ts (modified, +146/-0)
  • extensions/xai/src/tool-auth-shared.ts (modified, +121/-13)
  • src/agents/model-auth.test.ts (modified, +201/-0)
  • src/agents/model-auth.ts (modified, +64/-1)
  • src/cli/command-secret-targets.test.ts (modified, +2/-0)
  • src/cli/command-secret-targets.ts (modified, +1/-0)
  • src/cli/plugin-registry-loader.test.ts (modified, +17/-0)
  • src/cli/plugin-registry-loader.ts (modified, +10/-1)
  • src/commands/agents.providers.test.ts (added, +124/-0)
  • src/commands/agents.providers.ts (modified, +37/-1)
  • src/commands/status.scan.fast-json.test.ts (modified, +45/-0)
  • src/commands/status.scan.fast-json.ts (modified, +2/-0)
  • src/commands/status.scan.test.ts (modified, +18/-6)
  • src/config/types.secrets.resolution.test.ts (added, +80/-0)
  • src/config/types.secrets.ts (modified, +55/-7)
  • src/plugin-sdk/secret-input.ts (modified, +7/-1)
  • src/secrets/target-registry-data.ts (modified, +11/-0)
  • src/secrets/target-registry.test.ts (modified, +14/-0)

Code Example

{
  "source": "file",
  "provider": "localfile",
  "id": "/channels/feishu/appSecret"
}

---

openclaw health --json
openclaw status

---

[plugins] feishu failed during register ... Error: channels.feishu.appSecret: unresolved SecretRef "file:localfile:/channels/feishu/appSecret". Resolve this command against an active gateway runtime snapshot before reading it.
[openclaw] Failed to start CLI: PluginLoadFailureError: plugin load failed: feishu ...

---

--- src/cli/program/routes.ts
+++ src/cli/program/routes.ts
@@
- loadPlugins: (argv) => !hasFlag(argv, "--json"),
+ loadPlugins: false,

--- src/cli/plugin-registry.ts
+++ src/cli/plugin-registry.ts
@@
- const config = loadConfig();
+ const config = options?.config ?? loadConfig();

--- src/commands/status.ts
+++ src/commands/status.ts
@@
- ensurePluginRegistryLoaded({ scope: "configured-channels" });
+ ensurePluginRegistryLoaded({ scope: "configured-channels", config: cfg });

--- src/commands/status-json.ts
+++ src/commands/status-json.ts
@@
- ensurePluginRegistryLoaded({ scope: "configured-channels" });
+ ensurePluginRegistryLoaded({ scope: "configured-channels", config: cfg });
RAW_BUFFERClick to expand / collapse

Bug: openclaw status fails on file-backed SecretRef configs because route/plugin loading re-reads unresolved raw config

Summary

When a channel secret has been migrated out of ~/.openclaw/openclaw.json into a file-backed SecretRef, openclaw status can fail before the command reaches its own SecretRef resolution path.

In my case, channels.feishu.appSecret was migrated to:

{
  "source": "file",
  "provider": "localfile",
  "id": "/channels/feishu/appSecret"
}

The active gateway runtime is healthy and the secret resolves correctly there, but the local CLI status path crashes while loading plugins.

Affected Version

Environment

  • OS: Linux 6.17.0-19-generic
  • Node: 22.22.1
  • Install kind: global package install under ~/.npm-global/lib/node_modules/openclaw

Reproduction

  1. Configure a channel secret as a file-backed SecretRef in ~/.openclaw/openclaw.json.
  2. Store the actual secret value in ~/.openclaw/secrets.json.
  3. Ensure the gateway has already loaded the resolved config snapshot.
  4. Run:
openclaw health --json
openclaw status

Actual Behavior

openclaw health --json succeeds, but plain openclaw status fails with:

[plugins] feishu failed during register ... Error: channels.feishu.appSecret: unresolved SecretRef "file:localfile:/channels/feishu/appSecret". Resolve this command against an active gateway runtime snapshot before reading it.
[openclaw] Failed to start CLI: PluginLoadFailureError: plugin load failed: feishu ...

Expected Behavior

openclaw status should either:

  • resolve command SecretRefs before plugin loading, or
  • reuse the already-resolved config when plugin loading is needed

It should not re-read unresolved raw config and crash while plugins are registering.

Root Cause

There are two linked problems:

  1. src/cli/program/routes.ts

    • The route-first CLI path preloads plugins for plain status before the status command runs.
    • That means plugin registration happens before status has a chance to resolve SecretRefs via gateway.
  2. src/cli/plugin-registry.ts

    • ensurePluginRegistryLoaded() always calls loadConfig().
    • This discards the already-resolved config that status later computes via resolveCommandSecretRefsViaGateway(...).

The result is:

  • status has a valid secret-resolution path
  • but plugin loading bypasses it and re-opens the unresolved raw config

Why This Is Real And Not Just A Feishu Config Error

  • The same environment passes openclaw health --json.
  • The active gateway runtime can probe Feishu successfully.
  • openclaw status --json also succeeds after the minimal fix below.

So the issue is not "Feishu credentials are wrong"; it is specifically that the local CLI status path reads config through the wrong lifecycle.

Minimal Fix

  1. Add an optional config override to ensurePluginRegistryLoaded(...).
  2. In status and status --json, pass the resolved cfg into plugin loading.
  3. Disable route-level plugin preloading for plain status, so command-local SecretRef resolution happens first.

Minimal Patch

--- src/cli/program/routes.ts
+++ src/cli/program/routes.ts
@@
- loadPlugins: (argv) => !hasFlag(argv, "--json"),
+ loadPlugins: false,

--- src/cli/plugin-registry.ts
+++ src/cli/plugin-registry.ts
@@
- const config = loadConfig();
+ const config = options?.config ?? loadConfig();

--- src/commands/status.ts
+++ src/commands/status.ts
@@
- ensurePluginRegistryLoaded({ scope: "configured-channels" });
+ ensurePluginRegistryLoaded({ scope: "configured-channels", config: cfg });

--- src/commands/status-json.ts
+++ src/commands/status-json.ts
@@
- ensurePluginRegistryLoaded({ scope: "configured-channels" });
+ ensurePluginRegistryLoaded({ scope: "configured-channels", config: cfg });

Verification

Before the fix:

  • openclaw status -> fails with unresolved SecretRef during Feishu plugin registration

After the fix:

  • openclaw status -> succeeds
  • openclaw status --json -> succeeds
  • secretDiagnostics returns []

Follow-up

Plain openclaw health appears to have the same route-first plugin preload pattern, but it needs a separate fix because unlike status, health also depends on local channel plugin metadata during rendering.

The issue reported here is specifically the broken status CLI path and the underlying config lifecycle bug in route/plugin loading.

extent analysis

Fix Plan

To resolve the issue with openclaw status failing due to unresolved SecretRef configs, follow these steps:

  1. Modify ensurePluginRegistryLoaded function: Add an optional config override to allow passing a resolved config.
  2. Update status and status --json commands: Pass the resolved cfg into plugin loading using the modified ensurePluginRegistryLoaded function.
  3. Disable route-level plugin preloading for plain status: Set loadPlugins to false to ensure command-local SecretRef resolution happens first.

Code Changes

Apply the following patches:

--- src/cli/program/routes.ts
+++ src/cli/program/routes.ts
@@
- loadPlugins: (argv) => !hasFlag(argv, "--json"),
+ loadPlugins: false,

--- src/cli/plugin-registry.ts
+++ src/cli/plugin-registry.ts
@@
- const config = loadConfig();
+ const config = options?.config ?? loadConfig();

--- src/commands/status.ts
+++ src/commands/status.ts
@@
- ensurePluginRegistryLoaded({ scope: "configured-channels" });
+ ensurePluginRegistryLoaded({ scope: "configured-channels", config: cfg });

--- src/commands/status-json.ts
+++ src/commands/status-json.ts
@@
- ensurePluginRegistryLoaded({ scope: "configured-channels" });
+ ensurePluginRegistryLoaded({ scope: "configured-channels", config: cfg });

Verification

After applying the fix, verify that:

  • openclaw status succeeds
  • openclaw status --json succeeds
  • secretDiagnostics returns an empty array []

Extra Tips

  • Be cautious when modifying the ensurePluginRegistryLoaded function to avoid introducing unintended behavior.
  • Consider applying similar fixes to other commands that may be affected by the same issue, such as openclaw health.

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