Skip to content

[Bug]: openclaw message send fails with unresolved SecretRef for Discord channel token #33573

@jasonftl

Description

@jasonftl

Bug type

Behavior bug (incorrect output/state without crash)

Summary

openclaw message send fails with "unresolved SecretRef" when channels.discord.token is a file-based SecretRef. The resolution code at src/commands/message.ts:22 resolves the ref correctly, but the resolved config is discarded downstream: sendMessageDiscord (src/discord/send.outbound.ts:136) calls loadConfig() to load a fresh unresolved config from disk instead of using the resolved config threaded through the call chain.

Steps to reproduce

  1. Configure channels.discord.token as a SecretRef in openclaw.json:
    "token": { "source": "file", "provider": "filemain", "id": "/channels/discord/token" }
    with the token value stored in the file provider's secrets file, channels.discord.enabled: true, no accounts key.
  2. Start (or leave running) the gateway.
  3. Run: openclaw message send --channel discord --target "channel:XXXX" --message "test"

Reproduced 2026-03-03 on a clean v2026.3.2 install. 100% repro regardless of gateway state.

Expected behavior

The CLI resolves the SecretRef and delivers the message. The resolution code exists and is called.

Actual behavior

[plugins] plugins.allow is empty; discovered non-bundled plugins may auto-load: acpx ...
Error: channels.discord.token: unresolved SecretRef "file:filemain:/channels/discord/token".
Resolve this command against an active gateway runtime snapshot before reading it.

No [secrets] diagnostic lines are logged.

OpenClaw version

2026.3.2

Operating system

Debian Linux (6.12.63+deb13-amd64)

Install method

npm global

Logs, screenshots, and evidence

Config state at time of failure:

channels.discord.enabled:       true
channels.discord.accounts:      not present
channels.discord.token:         SecretRef object
secrets.defaults.file:          "filemain"
secrets.providers:              ["filemain"]

Root cause

messageCommand() at src/commands/message.ts:22 calls resolveCommandSecretRefsViaGateway(), which resolves the SecretRef and returns a config with the plaintext token. This resolved config is passed through runMessageAction -> handleSendAction -> executeSendAction -> sendMessage -> deliverOutboundPayloads -> the Discord outbound adapter.

At the final step, the Discord outbound adapter (src/channels/plugins/outbound/discord.ts:99) calls sendMessageDiscord(target, text, opts) without passing cfg in opts. Then sendMessageDiscord (src/discord/send.outbound.ts:136) calls const cfg = loadConfig() to load a fresh config from disk. This fresh config has the unresolved SecretRef. resolveDiscordAccount -> resolveDiscordToken -> normalizeDiscordToken -> assertSecretInputResolved throws.

The resolved config from the resolution chain is discarded. The resolution works but its result is never used.

Impact and severity

  • Affected: openclaw message send with any channel whose send function calls loadConfig() internally instead of accepting a resolved config. Confirmed for Discord; other channels may have the same pattern.
  • Severity: High - blocks message delivery entirely (actual token needed to authenticate).
  • Frequency: 100% repro on the message send + Discord direct adapter path when the token is a SecretRef. Other channels may have the same pattern but are not confirmed.
  • Consequence: Interactive CLI runs show a terminal error. Unattended cron jobs and scripts exit with code 1, causing silent message loss. The gateway stays healthy throughout.

Additional information

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions