Skip to content

updateLastRoute overwrites webchat session deliveryContext on every Telegram DM #13559

Description

@gmuniz21

Bug Description

When session.dmScope is set to per-channel-peer, every Telegram DM still calls updateLastRoute on agent:main:main (the webchat/dashboard session), overwriting its deliveryContext from webchat to telegram. This causes webchat responses to be routed to Telegram instead of back to the browser.

Steps to Reproduce

  1. Configure OpenClaw with Telegram + webchat enabled, session.dmScope: "per-channel-peer"
  2. Send a message via the webchat/dashboard UI — creates agent:main:main session with webchat delivery context
  3. Send a Telegram DM — the Telegram handler calls recordInboundSession() with updateLastRoute targeting route.mainSessionKey (agent:main:main)
  4. The agent:main:main session's deliveryContext is now { channel: "telegram", to: "<chatId>" }
  5. Next webchat response or heartbeat delivery reads the corrupted deliveryContext and routes to Telegram

Expected Behavior

When dmScope is per-channel-peer, Telegram DMs already get their own session key (agent:main:telegram:direct:<chatId>). The updateLastRoute call should NOT overwrite agent:main:main's deliveryContext, since the DM conversation is isolated by design.

Root Cause (from source analysis)

In reply-DptDUVRg.js (~line 36731), recordInboundSession() is called with:

updateLastRoute: !isGroup ? {
    sessionKey: route.mainSessionKey,  // Always "agent:main:main"
    channel: "telegram",
    to: String(chatId),
    ...
} : void 0

route.mainSessionKey is computed in resolveAgentRoute() (~line 18346) and always resolves to agent:main:main regardless of dmScope.

The updateLastRoute function in sandbox-DiYTfhXZ.js (~line 2505) then unconditionally overwrites the deliveryContext on the target session.

Self-Reinforcing Corruption

Once corrupted, the issue is self-reinforcing:

  • Heartbeat reads deliveryContext from agent:main:main → delivers to Telegram
  • initSessionState() (~line 64596) falls back to baseEntry?.lastChannel when ctx.OriginatingChannel is unset (heartbeat/cron contexts), preserving the corruption

Suggested Fix

When dmScope is not "main" (i.e., DMs have their own sessions), skip the updateLastRoute write to mainSessionKey. Alternatively, store "last route" metadata separately from the webchat session entry.

Workaround

We worked around this by:

  1. Setting explicit heartbeat.target: "telegram" + heartbeat.session: "heartbeat" to isolate heartbeat from the webchat session
  2. Running a monitoring script that detects and auto-fixes the corrupted deliveryContext

Environment

  • OpenClaw version: 2026.2.9 (33c75cb)
  • Node.js: v22
  • Channels: Telegram (polling) + webchat + Discord
  • Session config: dmScope: "per-channel-peer"

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions