Skip to content

fix(config): make status command resilient to unresolved SecretRefs#33213

Closed
Sid-Qin wants to merge 1 commit intoopenclaw:mainfrom
Sid-Qin:fix/33070-status-secretref-resilience
Closed

fix(config): make status command resilient to unresolved SecretRefs#33213
Sid-Qin wants to merge 1 commit intoopenclaw:mainfrom
Sid-Qin:fix/33070-status-secretref-resilience

Conversation

@Sid-Qin
Copy link
Copy Markdown
Contributor

@Sid-Qin Sid-Qin commented Mar 3, 2026

Summary

  • Problem: openclaw status crashed when channels.telegram.botToken used a SecretRef that couldn't be resolved (e.g., gateway unreachable).
  • Why it matters: Blocks production monitoring and status auditing when using SecretRef for tokens.
  • What changed: Wrapped per-account resolution in buildChannelsTable with try/catch. Unresolved accounts produce a degraded row with a warning instead of crashing the entire command.
  • What did NOT change: Secret resolution logic, gateway communication, or channel configuration.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • 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

User-visible / Behavior Changes

  • openclaw status shows degraded channel state instead of crashing on unresolved SecretRefs.

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Linux (Ubuntu)
  • Runtime: Node.js
  • Integration/channel: Telegram with SecretRef

Steps

  1. Configure botToken as SecretRef with file provider
  2. Run openclaw status when gateway is unreachable

Expected

  • Status shows channel in degraded state.

Actual

  • Before fix: Crash with "unresolved SecretRef" error.
  • After fix: Degraded row with warning.

Evidence

3 tests verify: degraded row on resolution failure, isolation between plugins, degraded accounts marked as disabled.

Human Verification (required)

  • Verified scenarios: unresolved SecretRef, resolved SecretRef (unchanged), multiple plugins
  • Edge cases checked: all accounts failing, partial failure
  • What I did not verify: All SecretRef providers

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Failure Recovery (if this breaks)

  • How to disable/revert: Remove try/catch wrapper
  • Files/config to restore: src/commands/status-all/channels.ts
  • Known bad symptoms: Status would crash again on unresolved SecretRefs

Risks and Mitigations

  • Risk: Degraded state masking real issues — Mitigated: warning message clearly indicates the cause

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 3, 2026

Greptile Summary

This PR wraps the per-account resolution loop in buildChannelsTable inside a try/catch block so that any exception (e.g., a SecretRef that can't be resolved) produces a graceful degraded row instead of crashing the entire status command. The change successfully prevents crashes and isolates failures per plugin.

Key concern: The catch block hardcodes lastError to "unresolved SecretRef (gateway unreachable)" regardless of the actual exception. Any unrelated runtime error inside the try body (null-pointer in resolveChannelAccountEnabled, timeout in resolveChannelAccountConfigured, unexpected shape from buildChannelAccountSnapshot, etc.) is silently reported as an unresolved-SecretRef problem, making real bugs invisible to operators. The actual error message should be preserved from the caught exception instead.

Confidence Score: 2/5

  • Safe to merge for the stated goal of preventing crashes on unresolved SecretRefs, but will silently mask unrelated runtime errors in production, harming observability and debugging.
  • The fix achieves its primary goal—preventing the status command from crashing on unresolved SecretRefs—and the approach is sound. However, the overly broad catch block that masks all exceptions with a hardcoded message is a correctness and observability concern. Operators will be misled about the root cause of failures, making troubleshooting harder and potentially obscuring real bugs. Fixing the catch block to preserve the actual error message is straightforward and would improve production diagnostics significantly.
  • src/commands/status-all/channels.ts — specifically the catch block (line 341–355) should preserve the actual exception message instead of hardcoding a misleading string.

Last reviewed commit: 6656ee0

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +341 to +355
} catch {
const degraded: ChannelAccountSnapshot = {
accountId,
enabled: false,
configured: false,
lastError: "unresolved SecretRef (gateway unreachable)",
};
accounts.push({
accountId,
account: {},
enabled: false,
configured: false,
snapshot: degraded,
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The catch block unconditionally sets lastError to the hardcoded string "unresolved SecretRef (gateway unreachable)" regardless of what actually threw. This masks unrelated runtime errors inside the try block (e.g., null-pointer in resolveChannelAccountEnabled, timeout in resolveChannelAccountConfigured, unexpected shape in buildChannelAccountSnapshot) and reports them to operators as a SecretRef problem, making real bugs invisible.

Preserve the actual error message instead:

Suggested change
} catch {
const degraded: ChannelAccountSnapshot = {
accountId,
enabled: false,
configured: false,
lastError: "unresolved SecretRef (gateway unreachable)",
};
accounts.push({
accountId,
account: {},
enabled: false,
configured: false,
snapshot: degraded,
});
}
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
const degraded: ChannelAccountSnapshot = {
accountId,
enabled: false,
configured: false,
lastError: errorMessage,
};
accounts.push({
accountId,
account: {},
enabled: false,
configured: false,
snapshot: degraded,
});
}

Since lastError will now carry the actual message, the hasDegradedAccount check on line 389 can also be simplified to accounts.some((a) => a.snapshot.lastError != null) rather than string-matching the hardcoded prefix.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/status-all/channels.ts
Line: 341-355

Comment:
The `catch` block unconditionally sets `lastError` to the hardcoded string `"unresolved SecretRef (gateway unreachable)"` regardless of what actually threw. This masks unrelated runtime errors inside the `try` block (e.g., null-pointer in `resolveChannelAccountEnabled`, timeout in `resolveChannelAccountConfigured`, unexpected shape in `buildChannelAccountSnapshot`) and reports them to operators as a SecretRef problem, making real bugs invisible.

Preserve the actual error message instead:

```suggestion
      } catch (e) {
        const errorMessage = e instanceof Error ? e.message : String(e);
        const degraded: ChannelAccountSnapshot = {
          accountId,
          enabled: false,
          configured: false,
          lastError: errorMessage,
        };
        accounts.push({
          accountId,
          account: {},
          enabled: false,
          configured: false,
          snapshot: degraded,
        });
      }
```

Since `lastError` will now carry the actual message, the `hasDegradedAccount` check on line 389 can also be simplified to `accounts.some((a) => a.snapshot.lastError != null)` rather than string-matching the hardcoded prefix.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6656ee0e94

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 393 to 395
if (!anyEnabled) {
return "off";
return hasDegradedAccount ? "warn" : "off";
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Surface degraded accounts even when another account is enabled

This branch only turns hasDegradedAccount into a warning when all accounts are disabled, so a plugin with mixed outcomes (one account throws during resolveAccount, another account resolves) can still report ok. Because degraded accounts are forced to enabled: false, they are excluded from enabledAccounts and token checks, and plugins that do not implement collectStatusIssues (for example Slack’s status block in extensions/slack/src/channel.ts:391-435) will not emit any warning path; the unresolved SecretRef is silently hidden instead of being surfaced in status output.

Useful? React with 👍 / 👎.

Replace the hardcoded "unresolved SecretRef" error message with the
actual caught error message so operators can distinguish between
SecretRef failures and other account resolution errors.

Closes openclaw#33070
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1092b3dd12

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 394 to 396
if (!anyEnabled) {
return "off";
return hasDegradedAccount ? "warn" : "off";
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Surface degraded account when any account resolution fails

This logic only considers hasDegradedAccount when !anyEnabled, so a plugin with mixed account outcomes (one account throws during resolveAccount, another account resolves and is enabled) can still report ok and hide the failed account from status output. In that scenario, channels without a collectStatusIssues implementation (such as Slack) have no alternate warning path, so unresolved SecretRef/account-resolution failures are silently masked. Fresh evidence: src/commands/status-all/channels.test.ts currently tests mixed plugin outcomes, but it does not cover mixed account outcomes within the same plugin, leaving this branch unverified.

Useful? React with 👍 / 👎.

@joshavant
Copy link
Copy Markdown
Contributor

Thanks for the report and the work here. This was addressed as part of a broader fix in #37023, which consolidated the related read-only SecretRef/status handling work into one implementation.

I’m closing this out as superseded by #37023 so the follow-up history stays in one place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands Command implementations size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Telegram botToken SecretRef unresolved in openclaw status (v2026.3.2)

2 participants