Repository navigation
Description
Bug Description
When Slack expires a native stream segment before the adapter stops it, stream() either continues in a new message or returns the expired one, and never edits the expired message. Slack shows a stream it closed itself as a "Something went wrong" pill. The thread keeps an error-looking message even though the reply was delivered.
Code on main (1e7a61e), packages/adapter-slack/src/index.ts:
- The
rotateSegmentcatch path ("stream segment expired before rotation, continuing in a new message") starts the next segment and leaves the expired one as is. - The final
stop()catch path ("stream-end blocks skipped" / "delivering the rest in a new message") returns or abandons the expired message without editing it. - The only
editMessageinstream()belongs to the post-and-edit fallback.
Steps to Reproduce
createSlackAdapter({ agentView: true })with the defaultstreamSegmentMaxAgeMs.thread.post(source), wheresourceyields some text, stays silent for 6 minutes (a long tool call), then yields more.- Slack expires the first segment during the gap. On the next delta,
rotateSegment'sstop()fails withmessage_not_in_streaming_state, and the reply continues in a new message. - Look at the first message.
Expected Behavior
The expired message keeps the content it already streamed, like a normally stopped segment.
Actual Behavior
The expired message stays as Slack sealed it, and Slack renders that as "Something went wrong". We found this by reading the code. The rendering comes from our direct API test and the reports below, not from a run through the adapter.
Chat SDK Version
4.41.0 (also on main @ 1e7a61e)
Platform Adapter
Slack
Additional Context
Evidence that a sealed, never-stopped stream renders as an error, and that chat.update repairs it:
- Our direct API test (28 Sep 2026, channel thread, plan mode): an append at ~3m45s worked. At ~9m25s,
chat.appendStreamandchat.stopStreamboth returnedmessage_not_in_streaming_state, and the message showed "Something went wrong" in place of the plan. message_not_in_streaming_stateafter undocumented timeouts hit slackapi/python-slack-sdk#1859 reports the same pill. Slack's fix, feat(web): expose public ts property on ChatStream slackapi/python-slack-sdk#1880, exposes the streamtsso apps can callchat.update, which it calls the only recovery path.- slack: native streams expire after about five minutes geminixiang/mikan#157:
chat.updateon an expired stream message succeeds, and users see the final answer. - fix(slack): keep progress updating after native streams expire openclaw/openclaw#141170: a real-API readback shows
chat.updaterewriting the same message, tasks included, aftermessage_not_in_streaming_state.
Suggested fix: in both expired paths, chat.update the expired message (segment.streamer.ts) with the content it already had, close any open task, then continue as today.
Related: #911 (a rotated segment with an in-progress task shows a red error icon; still on main, since rotateSegment's stop() sends no closing task_update), #884, #607.
Activity
n1shanthb commentedon Oct 19, 2026
Hey, I'd like to work on this.
I checked the current Slack
stream()implementation, and both therotateSegmentand finalstop()expiration handlers still leave the expired message without attempting a repair.I'm thinking of adding a best effort
chat.updateusing only that segment's confirmed text, without changing the existing recovery behavior if the update fails or there's no timestamp to update.I'd add regression coverage for both paths, including update failures, missing timestamps and unfinished code fences.
Should this also preserve task/plan content or would a text-only repair be an acceptable first scope?
Does this approach sound okay?