Skip to content

[Bug]: Block reply messages can arrive out of order due to parallel async tasks #399

@mukhtharcm

Description

@mukhtharcm

Description

Block streaming replies can arrive out of order in Telegram (and likely other providers). The first paragraph of a response may appear after subsequent paragraphs.

Steps to Reproduce

  1. Enable block streaming (default is on)
  2. Send a message that triggers a multi-paragraph response
  3. Observe that paragraphs may arrive in wrong order

Example

Agent generates:

Clawdbot. It's a tool built into the gateway.

Linux has similar concepts (background jobs with `&`, `nohup`, `screen`, `tmux`) but the `process` tool is Clawdbot's own abstraction that:

1. Wraps bash commands in managed sessions
2. Tracks them by sessionId
...

Telegram receives:

  1. "Linux has similar concepts..." (paragraph 2+)
  2. "Clawdbot. It's a tool built into the gateway." (paragraph 1)

The first paragraph arrived last.

Root Cause Analysis

In src/auto-reply/reply/agent-runner.ts around line 309, each block reply is wrapped in an async task:

const task = (async () => {
  if (!isHeartbeat) {
    await typing.startTypingOnText(cleaned);  // Variable delay based on text length
  }
  await opts.onBlockReply?.(blockPayload);
})()

These tasks are added to pendingBlockTasks with void (fire-and-forget), meaning they run in parallel:

pendingBlockTasks.add(task);
void task.finally(() => pendingBlockTasks.delete(task));

If Task 1 (first paragraph) has a longer typing delay than Task 2 (second paragraph), Task 2 may call sendBlockReply first, adding itself to the dispatcher chain before Task 1.

While the dispatcher uses a promise chain for serialization, the order tasks enter the chain depends on when they finish their typing delay — not when they were created.

Proposed Fix

Serialize block reply tasks instead of running in parallel:

// Instead of parallel fire-and-forget:
pendingBlockTasks.add(task);
void task.finally(...);

// Use sequential chaining:
blockReplyChain = blockReplyChain.then(() => task);

Or remove the typing delay from the critical path for block replies.

Environment

  • Clawdbot version: latest main
  • Provider: Telegram (likely affects all providers with block streaming)
  • Block streaming: enabled (default)

Workaround

Disable block streaming in config:

{
  "agent": {
    "blockStreamingDefault": "off"
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingquestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions