openclaw - ✅(Solved) Fix Slack download-file fails with SecretRef-based botToken (resolveToken reads raw config) [2 pull requests, 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#62088Fetched 2026-04-08 03:09:09
View on GitHub
Comments
1
Participants
2
Timeline
10
Reactions
0
Timeline (top)
cross-referenced ×5referenced ×4commented ×1

message(action="download-file") returns "File could not be downloaded (not found, too large, or inaccessible)" when channels.slack.accounts.default.botToken is configured as a SecretRef dict rather than a plain string.

Error Message

  1. Caller maps null → "not found or inaccessible" error

Root Cause

downloadSlackFile() in actions-ClxauASC.js calls resolveToken(opts.token, opts.accountId).

resolveToken calls loadConfig() which returns the raw config with unresolved SecretRef dicts, not the gateway's runtime snapshot where SecretRefs have been resolved to actual token strings.

The call chain:

  1. resolveToken(explicit, accountId)resolveSlackAccount({ cfg: loadConfig(), accountId })
  2. resolveSlackBotToken(explicit ?? account.botToken)account.botToken is the SecretRef dict
  3. normalizeResolvedSecretInputString({ value: <dict> })normalizeSecretInputString(<dict>) → returns undefined (not a string)
  4. Falls through to assertSecretInputResolved() which throws
  5. The throw is caught by catch { return null; } in resolveSlackMedia()
  6. Caller maps null → "not found or inaccessible" error

Fix Action

Fixed

PR fix notes

PR #62097: fix(slack): forward resolved botToken to downloadSlackFile

Description (problem / solution / changelog)

Closes #62088

When buildActionOpts returns undefined (default single-account setup, no token override), downloadSlackFile calls resolveToken(undefined, undefined) which re-reads raw config via loadConfig(). If botToken is a SecretRef object ({source: "env", key: "SLACK_BOT_TOKEN"}), normalizeResolvedSecretInputString rejects it because it expects a resolved string — the download fails with a misleading "bot token is required" error.

The fix injects the already-resolved botToken from the gateway runtime snapshot into the download call as a fallback when readOpts doesn't carry an explicit token. This mirrors the approach used for Discord's identical bug (b51214ec3e).

Note: The same resolveToken re-read pattern exists in other Slack action call sites (send, edit, read, pin, etc.) — those are candidates for a follow-up pass using the same token-forwarding pattern.

Testing: New test in action-runtime.test.ts verifies the resolved botToken reaches downloadSlackFile opts. All 37 existing tests pass.

🤖 Generated with Claude Code

Changed files

  • extensions/slack/src/action-runtime.test.ts (modified, +10/-0)
  • extensions/slack/src/action-runtime.ts (modified, +2/-0)

PR #62188: fix(slack): forward resolved token to downloadSlackFile

Description (problem / solution / changelog)

When botToken is configured as a SecretRef (e.g. { "$ref": "slack-bot-token" }), the downloadFile action fails with "File could not be downloaded (not found, too large, or inaccessible)" even though the file exists and the bot has access.

The problem is in the call chain:

  1. handleSlackAction resolves the account from the gateway's runtime config snapshot, where SecretRefs are already resolved to plain strings
  2. buildActionOpts("read") builds options for read operations — but when the resolved token equals botToken, it omits the token field (to avoid redundant overrides)
  3. downloadSlackFile receives opts without a token, calls resolveToken() internally, which calls loadConfig() — returning the raw config with unresolved SecretRef dicts
  4. resolveSlackBotToken gets the dict instead of a string, fails, and the error is caught and mapped to a null return

Other actions (sendMessage, readMessages, etc.) work because they don't re-resolve the token through loadConfig() in the same way.

Fix: explicitly pass the already-resolved token from getTokenForOperation("read") when calling downloadSlackFile, so its internal resolveToken receives a string and skips the raw config path.

Tests added:

  • bot token is forwarded when no userToken override exists
  • userToken is forwarded when configured
  • no token is injected when neither is available

Closes #62088

Changed files

  • .github/workflows/auto-response.yml (modified, +6/-2)
  • extensions/slack/src/action-runtime.test.ts (modified, +33/-0)
  • extensions/slack/src/action-runtime.ts (modified, +13/-4)
RAW_BUFFERClick to expand / collapse

Bug: download-file action fails with SecretRef-based botToken config

Summary

message(action="download-file") returns "File could not be downloaded (not found, too large, or inaccessible)" when channels.slack.accounts.default.botToken is configured as a SecretRef dict rather than a plain string.

Environment

  • OpenClaw version: 2026.4.5
  • macOS (arm64), Node v25.6.1
  • Slack socket mode, single account ("default")

Root Cause

downloadSlackFile() in actions-ClxauASC.js calls resolveToken(opts.token, opts.accountId).

resolveToken calls loadConfig() which returns the raw config with unresolved SecretRef dicts, not the gateway's runtime snapshot where SecretRefs have been resolved to actual token strings.

The call chain:

  1. resolveToken(explicit, accountId)resolveSlackAccount({ cfg: loadConfig(), accountId })
  2. resolveSlackBotToken(explicit ?? account.botToken)account.botToken is the SecretRef dict
  3. normalizeResolvedSecretInputString({ value: <dict> })normalizeSecretInputString(<dict>) → returns undefined (not a string)
  4. Falls through to assertSecretInputResolved() which throws
  5. The throw is caught by catch { return null; } in resolveSlackMedia()
  6. Caller maps null → "not found or inaccessible" error

Why other actions work

handleSlackAction() receives the account from the gateway runtime snapshot (SecretRefs resolved). It passes the resolved string through buildActionOpts(). But downloadSlackFile also calls resolveToken() internally, which re-reads raw config via loadConfig() — discarding the resolved token.

Only downloadSlackFile and other functions that call resolveToken()loadConfig() internally are affected. readMessages, sendMessage, etc. work fine because they use the pre-resolved token.

Verification

  • Direct fetch with the resolved bot token returns HTTP 200 with correct content
  • files.info API works correctly
  • Bot has files:read scope
  • No channel scope mismatch

Expected fix

downloadSlackFile should use the already-resolved token from the gateway snapshot rather than re-resolving from raw config. The resolved token is available in readOpts/writeOpts in handleSlackAction but isn't passed through when no explicit accountId or tokenOverride is set.

extent analysis

TL;DR

Modify the downloadSlackFile function to use the resolved token from the gateway snapshot instead of re-resolving from the raw config.

Guidance

  • Identify the downloadSlackFile function and modify it to accept the resolved token as a parameter.
  • Pass the resolved token from handleSlackAction to downloadSlackFile when calling it.
  • Verify that the readOpts and writeOpts in handleSlackAction contain the resolved token.
  • Update the resolveToken function to handle the case where the token is already resolved.

Example

// In handleSlackAction
const resolvedToken = readOpts.token;
downloadSlackFile(resolvedToken, ...);

// In downloadSlackFile
function downloadSlackFile(token, ...) {
  // Use the provided token instead of re-resolving
  ...
}

Notes

This fix assumes that the resolved token is available in the readOpts and writeOpts objects in handleSlackAction. If this is not the case, additional modifications may be needed to make the resolved token available to downloadSlackFile.

Recommendation

Apply the workaround by modifying the downloadSlackFile function to use the resolved token from the gateway snapshot. This should fix the issue without requiring an upgrade to a new version.

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