Summary
Every WebClient method takes **kwargs and forwards unrecognised keys into the request body
untouched. A one-character mistake in a parameter name therefore produces a request that is
missing the parameter you meant and carrying one nobody reads, with no exception and no warning.
For thread_ts nothing errors at all. A threaded reply becomes a top-level channel message and
every downstream check reports success.
Reproduction
Against slack_sdk 3.43.0, with the transport stubbed so the outgoing body is visible:
client.chat_postMessage(
channel="C1",
text="hi",
thread_id="1750000000.0001", # the parameter is thread_ts
unfurl_link=False, # the parameter is unfurl_links
)
Body actually sent:
{"thread_id": "1750000000.0001", "unfurl_link": false, "channel": "C1", "text": "hi"}
warnings raised: 0. thread_ts is absent from the body entirely, because it was None and
_remove_none_values stripped it. So the request is well formed, it just is not the request the
caller wrote.
Mechanism
In slack_sdk/web/client.py, chat_postMessage declares its known parameters and then does:
kwargs.update({ "channel": channel, "text": text, ..., "thread_ts": thread_ts, ... })
_parse_web_class_objects(kwargs)
kwargs = _remove_none_values(kwargs)
return self.api_call("chat.postMessage", json=kwargs)
thread_id arrived through **kwargs, survives _remove_none_values because it is not None,
and goes out with everything else. Nothing in the path compares the caller's keys against the
declared parameter list.
Why I think this deserves a fix
The declared parameters are already there, in the signature, which is what makes this cheap. The
method knows the full set of names it accepts. A caller who misses by one character is currently
told nothing at all, and the failure surfaces somewhere else, later, as a message in the wrong
place.
Three options, cheapest first:
- Warn on unknown keys.
warnings.warn naming the key and the method. Non-breaking, and it
makes the mistake visible in a test run.
- Warn harder on near misses. If an unknown key is within an edit distance of one or two of a
declared parameter, say which one you probably meant. thread_id to thread_ts,
unfurl_link to unfurl_links, file_content to content.
- Do nothing, but say so in the docstring, since the current behaviour is reasonable as an
escape hatch for API parameters the SDK has not caught up with. That is a real design reason
for **kwargs and I do not think it should be removed.
I would expect (1) or (2). Removing **kwargs would break the escape hatch and I am not
suggesting it.
A related one, same mechanism, different symptom
files_upload_v2(file_content=...) sends file_content through **kwargs and then raises
SlackRequestError: Any of file, content, and file_uploads must be specified.
which names three parameters the caller does not believe they omitted. Same root cause, and the
error message is actively misleading rather than merely absent.
How I found this, and what I am not claiming
I maintain a test harness that measures whether a coding model can drive a given SDK by running
the code it writes and asserting on the HTTP the SDK emits. slack_sdk 3.43.0 came out at the
top of everything I have measured: 16 tasks, 89 checks, three models at three attempts each, and
both frontier models passed all 89 checks on all 48 rollouts. So this is not a report that
models struggle with slack_sdk. They do not.
No model hit this bug. I found it by construction while checking whether my own scoring
could be gamed. That makes it latent, not measured, and I would rather say so than let it read
as a field report.
One limit on the repro above: my transport is stubbed, so what I can show is the body your SDK
sends. Whether Slack ignores thread_id and answers ok: true is your knowledge, not mine.
Happy to open a separate issue about files_upload's deprecation warning, which describes a
timeout risk ("may cause some issues like timeouts for relatively large files",
internal_utils.py:443) for an endpoint that has been sunset. Say the word rather than me
filing two at once.
toolshed is a small studio run by its owner, who directs the work, and AI does a lot of the
engineering.
Cal / toolshed / toolshedlabs@gmail.com
Summary
Every
WebClientmethod takes**kwargsand forwards unrecognised keys into the request bodyuntouched. A one-character mistake in a parameter name therefore produces a request that is
missing the parameter you meant and carrying one nobody reads, with no exception and no warning.
For
thread_tsnothing errors at all. A threaded reply becomes a top-level channel message andevery downstream check reports success.
Reproduction
Against slack_sdk 3.43.0, with the transport stubbed so the outgoing body is visible:
Body actually sent:
{"thread_id": "1750000000.0001", "unfurl_link": false, "channel": "C1", "text": "hi"}warnings raised: 0.thread_tsis absent from the body entirely, because it wasNoneand_remove_none_valuesstripped it. So the request is well formed, it just is not the request thecaller wrote.
Mechanism
In
slack_sdk/web/client.py,chat_postMessagedeclares its known parameters and then does:thread_idarrived through**kwargs, survives_remove_none_valuesbecause it is notNone,and goes out with everything else. Nothing in the path compares the caller's keys against the
declared parameter list.
Why I think this deserves a fix
The declared parameters are already there, in the signature, which is what makes this cheap. The
method knows the full set of names it accepts. A caller who misses by one character is currently
told nothing at all, and the failure surfaces somewhere else, later, as a message in the wrong
place.
Three options, cheapest first:
warnings.warnnaming the key and the method. Non-breaking, and itmakes the mistake visible in a test run.
declared parameter, say which one you probably meant.
thread_idtothread_ts,unfurl_linktounfurl_links,file_contenttocontent.escape hatch for API parameters the SDK has not caught up with. That is a real design reason
for
**kwargsand I do not think it should be removed.I would expect (1) or (2). Removing
**kwargswould break the escape hatch and I am notsuggesting it.
A related one, same mechanism, different symptom
files_upload_v2(file_content=...)sendsfile_contentthrough**kwargsand then raiseswhich names three parameters the caller does not believe they omitted. Same root cause, and the
error message is actively misleading rather than merely absent.
How I found this, and what I am not claiming
I maintain a test harness that measures whether a coding model can drive a given SDK by running
the code it writes and asserting on the HTTP the SDK emits.
slack_sdk3.43.0 came out at thetop of everything I have measured: 16 tasks, 89 checks, three models at three attempts each, and
both frontier models passed all 89 checks on all 48 rollouts. So this is not a report that
models struggle with slack_sdk. They do not.
No model hit this bug. I found it by construction while checking whether my own scoring
could be gamed. That makes it latent, not measured, and I would rather say so than let it read
as a field report.
One limit on the repro above: my transport is stubbed, so what I can show is the body your SDK
sends. Whether Slack ignores
thread_idand answersok: trueis your knowledge, not mine.Happy to open a separate issue about
files_upload's deprecation warning, which describes atimeout risk ("may cause some issues like timeouts for relatively large files",
internal_utils.py:443) for an endpoint that has been sunset. Say the word rather than mefiling two at once.
toolshed is a small studio run by its owner, who directs the work, and AI does a lot of the
engineering.
Cal / toolshed / toolshedlabs@gmail.com