Skip to content

SlackAdapter._upload_files passes channel_id= to slack-sdk files_upload_v2 → TypeError "got multiple values for keyword argument 'channel_id'" #102

Description

@patrick-chinchill

Summary

SlackAdapter._upload_files (chat_sdk/adapters/slack/adapter.py) calls client.files_upload_v2(channel_id=channel, ...). With recent slack-sdk (≥3.x), files_upload_v2's documented kwarg is channel, not channel_id. slack-sdk forwards arbitrary kwargs into its internal files_completeUploadExternal call AND adds its own channel_id there — producing a hard TypeError on every Slack file upload. No file upload via this adapter can succeed today.

Affected versions

  • chat-sdk-python v0.4.26.2 (pinned via chat-sdk[slack,teams,postgres] @ git+…@v0.4.26.2)
  • slack-sdk 3.37.0

Observed (production)

Logfire-captured stack from customer-ase-dev (chinchill-api app, 2026-05-28 02:56:24Z), reproducing on every chart-generating Slack reply:

TypeError: slack_sdk.web.async_client.AsyncWebClient.files_completeUploadExternal()
           got multiple values for keyword argument 'channel_id'

  File ".../chat_sdk/adapters/slack/adapter.py", line 2122, in post_message
    self._handle_slack_error(error)
  File ".../chat_sdk/adapters/slack/adapter.py", line 3045, in _handle_slack_error
    raise error
  File ".../chat_sdk/adapters/slack/adapter.py", line 2049, in post_message
    await self._upload_files(files, channel, thread_ts or None)
  File ".../chat_sdk/adapters/slack/adapter.py", line 2643, in _upload_files
    result = await client.files_upload_v2(**kwargs)
  File ".../slack_sdk/web/async_client.py", line 4040, in files_upload_v2
    completion = await self.files_completeUploadExternal(...)
TypeError: ... got multiple values for keyword argument 'channel_id'

Root cause

chat_sdk/adapters/slack/adapter.py lines 2638-2643 (in _upload_files):

client = self._get_client()
kwargs: dict[str, Any] = {"channel_id": channel, "file_uploads": file_uploads}  # ← bug: should be "channel"
if thread_ts:
    kwargs["thread_ts"] = thread_ts

result = await client.files_upload_v2(**kwargs)

slack-sdk's AsyncWebClient.files_upload_v2 (slack_sdk/web/async_client.py:~3950) signature uses channel: Optional[str]. Any extra kwargs (including channel_id) are passed through to the internal files_completeUploadExternal call (which derives its own channel_id from channel) — colliding.

One-line fix

Change "channel_id" → "channel" on the kwargs dict at line 2639. Any internal references to that key elsewhere in the function need the same rename (none observed in this code path).

- kwargs: dict[str, Any] = {"channel_id": channel, "file_uploads": file_uploads}
+ kwargs: dict[str, Any] = {"channel": channel, "file_uploads": file_uploads}

(Apply the same fix to any other adapter helper that wraps files_upload_v2 — SlackAdapter had only one such site when I checked v0.4.26.2.)

Workaround (consumer-side, in case anyone else hits this before a release)

Bypass _upload_files by extracting the underlying AsyncWebClient and calling slack-sdk directly with the correct kwarg:

adapter = post_target.adapter
client = adapter._get_client()
decoded = adapter.decode_thread_id(post_target.id)
await client.files_upload_v2(
    channel=decoded.channel,
    thread_ts=decoded.thread_ts or None,
    file_uploads=[{"file": bytes_, "filename": name} for ...],
    initial_comment=...,
)

We've shipped this bypass in Chinchill-AI/chinchill-api#1138 so production isn't waiting on a release here, but we'd love to drop the bypass once the upstream fix lands.

Suggested follow-ups

  1. Add a test that asserts _upload_files passes channel= (not channel_id=) to the slack client (mock the client, assert call args). Catches any future rename.
  2. Audit other adapter call sites for similar kwarg-rename drift between slack-sdk releases (files.upload → files_upload_v2 was a known cleanup that shifted naming around).

Happy to send a PR if useful — let me know.

Activity

  1. coderabbitai commented on May 28, 2026

    @coderabbitai
    🔗 Related PRs

    #48 - fix: adapter hygiene — Slack stream await, Teams divider, public worker APIs [merged]
    #49 - chore(types): pyrefly 213 → 0 errors + fix 3 latent runtime bugs [merged]
    #60 - fix(chat): enforce ConcurrencyConfig.max_concurrent + release 0.4.26.1 [merged]
    #67 - feat(attachments): port rehydrateAttachment hook (#52) [merged]
    #82 - fix(slack): render_postable uses AST path for PostableMarkdown (closes #81) [merged]


    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  2. added a commit that references this issue on May 28, 2026
    ff27651
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingslack

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions