fix(slack): guard Slack API calls against empty threadTs to fix invalid_thread_ts#292
Conversation
|
@chechunhsu is attempting to deploy a commit to the Vercel Labs Team on Vercel. A member of the Team first needs to authorize it. |
|
Thanks for the fix, the However, the empty threadTs for top-level DMs was intentional (added in #39) so that The issue author actually suggested a two-part fix: use |
30d48d8 to
a6601ca
Compare
|
Thanks for the detailed feedback! You're right — the empty I've updated the PR to take the two-part approach you suggested: keep the existing DM threadTs logic unchanged, and guard the Slack API calls against empty strings instead. Specifically, each method that calls Slack APIs ( const { channel, threadTs: rawThreadTs } = this.decodeThreadId(threadId);
const threadTs = rawThreadTs || undefined;This follows the pattern |
|
Thanks for the updated approach, guarding with Two things on the
|
…id_thread_ts Preserve the intentional empty threadTs for top-level DMs (added in vercel#39 for openDM() subscription matching) while preventing Slack API errors. Instead of changing the threadTs logic, normalize empty threadTs to undefined at the entry of each method that calls Slack APIs (postMessage, postEphemeral, scheduleMessage, stream). This way: - openDM() subscription matching continues to work (empty threadTs) - Slack API calls receive undefined instead of "" (no invalid_thread_ts) The stream method throws ValidationError on empty threadTs (matching startTyping's early-return pattern) so TypeScript narrows correctly without any `as string` casts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
a6601ca to
6570e06
Compare
|
Updated:
Zero |
…, and postEphemeral
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…id_thread_ts (vercel#292) * fix(slack): guard Slack API calls against empty threadTs to fix invalid_thread_ts Preserve the intentional empty threadTs for top-level DMs (added in vercel#39 for openDM() subscription matching) while preventing Slack API errors. Instead of changing the threadTs logic, normalize empty threadTs to undefined at the entry of each method that calls Slack APIs (postMessage, postEphemeral, scheduleMessage, stream). This way: - openDM() subscription matching continues to work (empty threadTs) - Slack API calls receive undefined instead of "" (no invalid_thread_ts) The stream method throws ValidationError on empty threadTs (matching startTyping's early-return pattern) so TypeScript narrows correctly without any `as string` casts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add tests for empty threadTs normalization in stream, scheduleMessage, and postEphemeral --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
…id_thread_ts (#292) * fix(slack): guard Slack API calls against empty threadTs to fix invalid_thread_ts Preserve the intentional empty threadTs for top-level DMs (added in #39 for openDM() subscription matching) while preventing Slack API errors. Instead of changing the threadTs logic, normalize empty threadTs to undefined at the entry of each method that calls Slack APIs (postMessage, postEphemeral, scheduleMessage, stream). This way: - openDM() subscription matching continues to work (empty threadTs) - Slack API calls receive undefined instead of "" (no invalid_thread_ts) The stream method throws ValidationError on empty threadTs (matching startTyping's early-return pattern) so TypeScript narrows correctly without any `as string` casts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add tests for empty threadTs normalization in stream, scheduleMessage, and postEphemeral --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Bundles 5 small upstream bug fixes into one PR. Each is independent and covered by a regression test. - vercel/chat#394 (slack): preserve email addresses in @mention regex. ``user@domain.com`` no longer extracts ``@domain`` as a mention. - vercel/chat#292 (slack): guard Slack API calls against empty ``thread_ts`` to fix ``invalid_thread_ts`` errors. - vercel/chat#256 (discord): remove duplicate text when posting card messages. ``content`` is omitted on the create path and explicitly cleared on the edit path (Discord PATCH preserves omitted fields). - vercel/chat#395 (slack): enrich link previews with unfurl metadata from attachments. Routes ``message_changed`` events through a new ``_handle_message_changed`` so the message handler sees unfurled links instead of bare URLs. New cache + poll window. - vercel/chat#407 (telegram): rewrite format converter to emit MarkdownV2 (``*bold*`` etc.) instead of legacy ``Markdown``. Adds proper escaping for the 18 special characters MarkdownV2 reserves in normal text, the narrower set inside code blocks, and the parens/backslash escape inside link URLs. https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
#94) The vercel/chat#292 empty-thread_ts guard correctly avoided chat.startStream's invalid_thread_ts rejection, but raising ValidationError silently dropped top-level Slack DM streaming replies because Thread._handle_stream does not catch and _fallback_stream only triggers when the adapter has no stream method at all. Replace the raise with the production-tested accumulate-and-post path Tony described in chat-sdk-python#94: degrade DMs to a single non-streamed post_message call (Slack accepts an empty thread_ts on chat.postMessage) and keep native streaming for channels. This lets chinchill-api retire its MultiTenantSlackAdapter subclass override.
Summary
Fixes #268
The empty
threadTsfor top-level DMs was intentionally added in #39 so thatopenDM()subscriptions match incoming DM messages. The previous approach of changing the threadTs logic toevent.thread_ts || event.tsfixed the API error but broke subscription matching — DMs routed toonNewMention()instead ofonSubscribedMessage().This PR takes the two-part approach suggested in review feedback: preserve the empty threadTs for DM subscription matching, and guard Slack API calls so empty strings don't cause
invalid_thread_tserrors.What changed
Each method that calls Slack APIs (
postMessage,postEphemeral,scheduleMessage,stream) now normalizesthreadTsat entry:This follows the same pattern
postEphemeralalready used (thread_ts: threadTs || undefined), but applied once at method entry instead of at each call site.Result
openDM()subscription matching continues to work (empty threadTs in threadId)undefinedinstead of""→ noinvalid_thread_tserrorsTest plan
pnpm test --filter=@chat-adapter/slack— 297 tests passpnpm test --filter=@chat-adapter/integration-tests—replay-dm.test.ts17 tests pass (DM subscription matching verified)pnpm turbo build --filter=@chat-adapter/slack— builds cleanly with no type errors