Skip to content

doctor fails on exec SecretRefs despite running gateway with resolved snapshot #50161

Description

@lolisaigao1234

Summary

openclaw doctor fails with unresolved SecretRef errors for channel credentials managed by an exec provider (credexec), even when the gateway is running and has successfully resolved all secrets. This blocks openclaw update, which gates on doctor.

Reproduction

Environment: openclaw 2026.3.13, Linux (WSL2), systemd user service, Node 22.22.0

Config (simplified):

{
  "channels": {
    "telegram": {
      "enabled": true,
      "botToken": {
        "source": "exec",
        "provider": "credexec",
        "id": "channels/telegram/botToken"
      }
    }
  },
  "gateway": {
    "bind": "loopback",
    "auth": {
      "mode": "token",
      "token": {
        "source": "exec",
        "provider": "credexec",
        "id": "gateway/auth/token"
      }
    }
  }
}

Steps:

  1. Gateway is running and healthy (openclaw gateway statusRPC probe: ok)
  2. openclaw secrets reloadSecrets reloaded.
  3. openclaw secrets auditunresolved=0 (all secrets resolved)
  4. openclaw status → Channels show OK with resolved token info
  5. openclaw doctorfails:
Error: channels.telegram.botToken: unresolved SecretRef "exec:credexec:channels/telegram/botToken".
Resolve this command against an active gateway runtime snapshot before reading it.
  1. openclaw updatefails at the openclaw doctor step (the actual package update succeeds):
Steps:
  ✓ global update (21.68s)
  ✗ openclaw doctor (3.56s)
      Error: channels.telegram.botToken: unresolved SecretRef ...

Expected behavior

Per docs/gateway/secrets.md (lines 346–358), doctor is a read-only command path that should:

also prefer the active snapshot, but degrade instead of aborting when a targeted SecretRef is unavailable in that command path.

And per docs/gateway/doctor.md (lines 267–273):

openclaw doctor --fix now uses the same read-only SecretRef summary model as status-family commands for targeted config repairs.
If the Telegram bot token is configured via SecretRef but unavailable in the current command path, doctor reports that the credential is configured-but-unavailable and skips auto-resolution instead of crashing.

In practice, doctor hits the strict assertSecretInputResolved() path (in types.secrets-BvNoSfUf.js:56) which throws unconditionally when it encounters a SecretRef object — it never queries the gateway's secrets.resolve RPC or falls back to degraded output.

openclaw status handles the same SecretRefs correctly (shows resolved token info from the gateway snapshot), confirming the gateway RPC works.

Actual behavior

Doctor's config-parsing code calls assertSecretInputResolved()resolveSecretInputRef() before ever attempting gateway RPC resolution. If the config value is a SecretRef object (has source/provider/id fields), the assertion throws immediately. The documented degraded read-only path is never reached.

Impact

  • openclaw update is completely blocked — it gates on doctor post-check
  • Any setup using exec-backed SecretRefs for channel credentials cannot pass doctor
  • Workaround: replace all SecretRef objects with resolved plaintext values in openclaw.json, defeating the purpose of the credential delegation model

Notes

  • The error message itself is misleading — it says "Resolve this command against an active gateway runtime snapshot" but the gateway IS running with a fully resolved snapshot. The issue is that doctor doesn't query it.
  • openclaw doctor --fix --yes exhibits the same behavior (no degraded path).
  • openclaw secrets audit --check correctly reports unresolved=0.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions