[Bug]: Discord chunked send drops chunks silently on transient errors (regression in 2026.5.12 via sendDurableMessageBatch)
Related (closed-as-not-planned): #32887
TL;DR
After upgrading from [email protected] → [email protected] (with @openclaw/[email protected]), long agent replies that are correctly split into multiple Discord chunks by the chunker only have chunk 1 delivered to Discord. Chunks 2+ silently never appear. No 429/5xx error is logged. The transcript stored in the session jsonl shows the full reply.
This is the same symptom class as #32887 (which targeted the old sendMessageDiscord path), but in the new sendDurableMessageBatch delivery wrapper introduced in 2026.5.12. #32887 was stale-closed; this looks like a fresh recurrence in a different code path.
Reproduction
- On openclaw 2026.5.12 + @openclaw/discord 2026.5.12, with any agent backed by a model that produces multi-paragraph replies (Codex/gpt-5.5 in my case).
- Send the agent a prompt that elicits a >2000 char or >17 line response into a Discord DM (e.g. "Brainstorm 5 ways you could be more proactive — explain each in 2-3 sentences").
- Observe: Discord receives only the first chunk. No follow-up message, no error.
Concrete observed case
- Session: Discord DM (
agent:main:discord:default:direct:...)
- Model output: 2,060 chars, 30 lines
- Chunker (verified by extracting and running it standalone on the actual text):
- Produces 2 chunks: 1,049 chars / 17 lines and 1,010 chars / 13 lines
- Discord channel: only chunk 1 delivered. No chunk 2.
- Gateway journal at the send timestamp: no rate-limit, no HTTP error, no
partial_failed, no Discord send failed. Only side-effect logs:
[plugins] [aight-utils] agent_end fired session=...
[plugins] [aight-utils] Push sent: ... ok=true (x2 mobile devices)
(mobile push notifications fired ok — those are a separate aight-utils plugin, not the Discord channel post)
Regression bisect
Diffed @openclaw/discord dist/ between 2026.5.7 (works) and 2026.5.12 (broken):
buildDiscordTextChunks() — identical
chunkDiscordTextWithMode() / chunkDiscordText() — identical (constants DEFAULT_MAX_CHARS=2000, DEFAULT_MAX_LINES=17 unchanged)
sendDiscordText() — identical body (loops over chunks calling sendChunk → request(() => createChannelMessage(...)))
deliverDiscordReply() — changed:
- 2026.5.7:
await deliverOutboundPayloads({ payloads, ... }) — simple list of results
- 2026.5.12:
await sendDurableMessageBatch({ payloads, ... }) — new durable wrapper with {status, results, error} shape, including a partial_failed branch that throws
The new sendDurableMessageBatch is in openclaw/dist/send-BvX1IpyO.js (lines ~168+). It goes through withDurableMessageSendContext → ctx.render() → ctx.send() → deliverOutboundPayloadsInternal(). Somewhere on this path, chunks 2+ appear to be dropped before reaching the per-chunk HTTP call, with no error surfaced at INFO level.
I didn't have time to instrument deliverOutboundPayloadsInternal directly to pinpoint the drop. My guess (untested): the rendered batch plan treats the multi-chunk payload as a single rendered unit, or onPayloadDeliveryOutcome only fires for the first payload before something short-circuits the rest.
Local workaround applied
Patched send.shared-*.js's sendDiscordText to wrap each per-chunk sendChunk() in a retry-with-backoff (3 attempts, honors retry-after, retries 429 + 5xx), AND to continue best-effort if a single chunk permanently fails (logs the failure loudly instead of throwing silently). This restores end-to-end multi-chunk delivery for the symptom but does not fix the underlying drop in the durable wrapper.
Patch is reapplied by a small post-openclaw update hook since plugin reinstall otherwise clobbers it.
Environment
- openclaw: 2026.5.12
- @openclaw/discord: 2026.5.12
- Model: openai-codex/gpt-5.5 (ChatGPT Pro via OAuth)
- OS: Linux (Debian-based VM)
- Node: v22.19.0
What would help
- Confirmation of the drop location in
deliverOutboundPayloadsInternal / the durable wrapper
- Whether 2026.5.14-beta.* or 2026.5.16-beta.* already addresses this
- Whether the chunk fan-out is supposed to happen at the durable-payload layer or at the channel-plugin layer (the 2026.5.7→2026.5.12 refactor seems to have moved the boundary; possibly chunks are no longer being propagated)
Filed via Claude Code on behalf of @Piste — investigation and patch authored by the AI agent against my live install. Happy to gather additional traces or test fixes against my environment.
[Bug]: Discord chunked send drops chunks silently on transient errors (regression in 2026.5.12 via
sendDurableMessageBatch)Related (closed-as-not-planned): #32887
TL;DR
After upgrading from
[email protected]→[email protected](with@openclaw/[email protected]), long agent replies that are correctly split into multiple Discord chunks by the chunker only have chunk 1 delivered to Discord. Chunks 2+ silently never appear. No 429/5xx error is logged. The transcript stored in the session jsonl shows the full reply.This is the same symptom class as #32887 (which targeted the old
sendMessageDiscordpath), but in the newsendDurableMessageBatchdelivery wrapper introduced in 2026.5.12. #32887 was stale-closed; this looks like a fresh recurrence in a different code path.Reproduction
Concrete observed case
agent:main:discord:default:direct:...)partial_failed, noDiscord send failed. Only side-effect logs:Regression bisect
Diffed
@openclaw/discorddist/between 2026.5.7 (works) and 2026.5.12 (broken):buildDiscordTextChunks()— identicalchunkDiscordTextWithMode()/chunkDiscordText()— identical (constantsDEFAULT_MAX_CHARS=2000,DEFAULT_MAX_LINES=17unchanged)sendDiscordText()— identical body (loops over chunks callingsendChunk→request(() => createChannelMessage(...)))deliverDiscordReply()— changed:await deliverOutboundPayloads({ payloads, ... })— simple list of resultsawait sendDurableMessageBatch({ payloads, ... })— new durable wrapper with{status, results, error}shape, including apartial_failedbranch that throwsThe new
sendDurableMessageBatchis inopenclaw/dist/send-BvX1IpyO.js(lines ~168+). It goes throughwithDurableMessageSendContext→ctx.render()→ctx.send()→deliverOutboundPayloadsInternal(). Somewhere on this path, chunks 2+ appear to be dropped before reaching the per-chunk HTTP call, with no error surfaced at INFO level.I didn't have time to instrument
deliverOutboundPayloadsInternaldirectly to pinpoint the drop. My guess (untested): the rendered batch plan treats the multi-chunk payload as a single rendered unit, oronPayloadDeliveryOutcomeonly fires for the first payload before something short-circuits the rest.Local workaround applied
Patched
send.shared-*.js'ssendDiscordTextto wrap each per-chunksendChunk()in a retry-with-backoff (3 attempts, honorsretry-after, retries 429 + 5xx), AND to continue best-effort if a single chunk permanently fails (logs the failure loudly instead of throwing silently). This restores end-to-end multi-chunk delivery for the symptom but does not fix the underlying drop in the durable wrapper.Patch is reapplied by a small post-
openclaw updatehook since plugin reinstall otherwise clobbers it.Environment
What would help
deliverOutboundPayloadsInternal/ the durable wrapperFiled via Claude Code on behalf of @Piste — investigation and patch authored by the AI agent against my live install. Happy to gather additional traces or test fixes against my environment.