← Back to PRs

#17558: fix(slack): replace files.uploadV2 with 3-step upload flow to fix missing_scope error

by fif911 open 2026-02-15 22:18 View on GitHub →
channel: slack size: S
## Problem `uploadSlackFile()` uses `client.files.uploadV2()` from `@slack/web-api`, which internally calls the **deprecated `files.upload`** endpoint. This fails with `missing_scope` even when `files:write` is correctly granted in the bot token scopes. The direct REST API (`files.getUploadURLExternal` → upload → `files.completeUploadExternal`) works fine with the same token, confirming the scope is present but the SDK method uses a different code path. ## Fix Replace the single `files.uploadV2()` call with Slack's recommended 3-step upload flow: 1. `files.getUploadURLExternal` — get presigned upload URL + file_id 2. `fetch(upload_url)` — upload file content to presigned URL 3. `files.completeUploadExternal` — finalize & share to channel/thread ## Changes - **1 file changed**: `src/slack/send.ts` - Only the `uploadSlackFile()` function body is modified - Function signature unchanged - All existing behavior preserved (channel uploads, thread replies via `thread_ts`, captions via `initial_comment`) - Removed unused `FilesUploadV2Arguments` type import ## Testing Manually verified the 3-step flow works correctly: - ✅ Channel upload (file shared to channel with caption) - ✅ Thread reply upload (file shared in thread with `thread_ts`) - ✅ Same bot token, same `files:write` scope — no more `missing_scope` error <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR replaces the broken `files.uploadV2()` SDK call in `uploadSlackFile()` with Slack's recommended 3-step upload flow: `getUploadURLExternal` → presigned URL POST → `completeUploadExternal`. The change fixes `missing_scope` errors that occurred because `files.uploadV2` internally uses the deprecated `files.upload` endpoint. - The implementation is clean and well-structured with explicit error handling at each step - Function signature and all existing behavior (channel uploads, thread replies via `thread_ts`, captions via `initial_comment`) are preserved - The return value semantics remain the same — both old and new code return a Slack file ID, not a message timestamp. This is a pre-existing limitation where `messageId` in `SlackSendResult` won't be a valid Slack `ts` for file-only uploads (affecting downstream operations like reactions/edits), but this PR does not change that behavior - The unused `FilesUploadV2Arguments` import is correctly removed - CHANGELOG entry is accurate <h3>Confidence Score: 4/5</h3> - This PR is safe to merge — it fixes a real Slack API compatibility issue with a well-structured replacement that preserves all existing behavior. - Score of 4 reflects that this is a focused, well-implemented fix for a known Slack SDK issue. The 3-step upload flow follows Slack's recommended approach, error handling is thorough, and existing behavior is preserved. Minor deduction because the Content-Type header is not forwarded to the presigned URL upload (style concern, not a functional bug). - No files require special attention. The change is isolated to the `uploadSlackFile` function body in `src/slack/send.ts`. <sub>Last reviewed commit: 30ee226</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs