Summary
On Slack, thread.post(asyncIterable) opens one native streaming message and appends each chunk. Slack expires a message's streaming state when appends stop flowing for a while (it's designed for active token streams). With a slow producer — e.g. an agent that emits a message, runs tools for a few minutes, then emits the next one — the next append after a long gap throws message_not_in_streaming_state, the error propagates out of thread.post, and every remaining chunk is lost.
Repro
await thread.post((async function* () {
yield "part one"
await new Promise((r) => setTimeout(r, 5 * 60_000))
yield "part two" // throws message_not_in_streaming_state
})())
Error: An API error occurred: message_not_in_streaming_state
at ChatStreamer.flushBuffer (@slack/web-api/src/chat-stream.ts)
at ChatStreamer.append
at flushMarkdownDelta (@chat-adapter/slack)
at _SlackAdapter.stream
at _ThreadImpl.handleStream
Expected
The docs say any AsyncIterable<string> works, with no documented cadence constraint — and the SDK already has post+edit / buffered fallbacks for other platforms. When the stream state expires, the adapter could finalize what was streamed and deliver the remaining chunks via the post+edit fallback (or a fresh streamed message), instead of throwing away the tail.
Context
Hit this wiring a long-running agent (minutes-long tool calls between messages) to Slack via the Chat SDK.
Summary
On Slack,
thread.post(asyncIterable)opens one native streaming message and appends each chunk. Slack expires a message's streaming state when appends stop flowing for a while (it's designed for active token streams). With a slow producer — e.g. an agent that emits a message, runs tools for a few minutes, then emits the next one — the next append after a long gap throwsmessage_not_in_streaming_state, the error propagates out ofthread.post, and every remaining chunk is lost.Repro
Expected
The docs say any
AsyncIterable<string>works, with no documented cadence constraint — and the SDK already has post+edit / buffered fallbacks for other platforms. When the stream state expires, the adapter could finalize what was streamed and deliver the remaining chunks via the post+edit fallback (or a fresh streamed message), instead of throwing away the tail.Context
Hit this wiring a long-running agent (minutes-long tool calls between messages) to Slack via the Chat SDK.