Skip to content

Telegram DM spooled update hot-loops on reply session initialization conflict in 2026.6.11-beta.1 #96837

Description

@CnBarrier404

Summary

On a Linux systemd-user gateway running OpenClaw 2026.6.11-beta.1 (c862a64), a Telegram direct-message update became stuck in channel_ingress_events as pending and was retried continuously after every replay failed with:

reply session initialization conflicted for agent:main:telegram:direct:<telegram-user-id>

The gateway itself stayed healthy and reachable, but Telegram DM processing was effectively blocked behind the poisoned pending update. A later Telegram update for the same DM remained pending with attempts=0 because the earlier update never cleared.

This looks related to #96698 / #96699 and #96550, but this report is for a Telegram DM on 2026.6.11-beta.1, where the retry loop is still observable in the installed npm runtime.

Environment

  • OpenClaw: 2026.6.11-beta.1 (c862a64)
  • Node: v26.1.0 via nvm
  • OS: Linux 7.1.1-070101-generic x64
  • Service: systemd user service openclaw-gateway.service
  • Gateway mode: local loopback
  • Gateway port: 24619
  • Channel: Telegram direct message, polling / isolated ingress
  • Config shape: channels.telegram.dmPolicy = allowlist, one allowed sender

Gateway health was OK:

curl http://127.0.0.1:24619/health
{"ok":true,"status":"live"}

Service was active:

openclaw-gateway.service loaded active running OpenClaw Gateway (v2026.6.11-beta.1)
ExecStart=.../openclaw/dist/index.js gateway --port 24619

Only one OpenClaw gateway process was running.

Observed behavior

Gateway logs repeatedly showed the same Telegram update being replayed and failing:

[telegram] Inbound message telegram:<user-id> -> @<bot> (direct, 3 chars)
[diagnostic] message dispatch completed: channel=telegram sessionId=unknown sessionKey=agent:main:telegram:direct:<user-id> source=replyResolver outcome=error duration=... error="Error: reply session initialization conflicted for agent:main:telegram:direct:<user-id>"
[diagnostic] message processed: channel=telegram chatId=telegram:<user-id> messageId=<message-id> sessionId=<session-id> sessionKey=agent:main:telegram:direct:<user-id> outcome=error duration=... error="Error: reply session initialization conflicted for agent:main:telegram:direct:<user-id>"
[telegram] dispatch failed: Error: reply session initialization conflicted for agent:main:telegram:direct:<user-id>
[telegram] [diag] spooled update <update-id> failed; keeping for retry: TelegramSpooledReplayProcessingError in middleware: telegram spooled update processing failed: Error: reply session initialization conflicted for agent:main:telegram:direct:<user-id>

The same messageId was logged over and over. During a short watch, the same inbound message was retried about twice per second.

channel_ingress_events showed two pending Telegram updates for the same account:

queue_name              event_id          channel_id  account_id  status   attempts  last_attempt_at
----------------------  ----------------  ----------  ----------  -------  --------  ---------------
["telegram","default"]  <update-id-1>    telegram    default     pending  900+      <recent time>
["telegram","default"]  <update-id-2>    telegram    default     pending  0         null

The second update was not processed because the first pending update kept re-entering the retry loop.

The file spool directory was empty:

~/.openclaw/telegram/ingress-spool-default
# no files

So the stuck state was in SQLite channel_ingress_events, not an on-disk spool file.

Local investigation notes

The thrown error comes from the reply session initialization path:

// dist/get-reply-*.js
if (!committed.ok) {
  if (!staleSnapshotRetried) return await initSessionStateAttempt(params, true);
  throw new Error(`reply session initialization conflicted for ${sessionKey}`);
}

commitReplySessionInitialization() compares a full-entry revision:

function createReplySessionInitializationRevision(entry) {
  return JSON.stringify(entry ?? null);
}

The Telegram retry path treats this as retryable and releases the update back to pending:

[telegram][diag] spooled update <update-id> failed; keeping for retry: TelegramSpooledReplayProcessingError ... reply session initialization conflicted ...

At the time of inspection, resolveNonRetryableSpooledUpdateFailure() only recognized a small set of non-retryable cases, so this conflict stayed retryable and hot-looped.

Why this may not be fully covered by existing reports

Related existing items:

This report adds a current Telegram DM repro on 2026.6.11-beta.1 (c862a64), with the stuck update in SQLite channel_ingress_events, not file spool, and with a direct DM session key:

agent:main:telegram:direct:<user-id>

If #96550/#96699 are expected to cover this, it would be useful to know whether they have landed in a release newer than 2026.6.11-beta.1, or whether Telegram DM + SQLite ingress still needs separate coverage.

Expected behavior

A transient reply-session initialization conflict should not permanently block the Telegram DM lane.

At minimum, one of these should happen:

  1. retry with bounded backoff instead of immediate hot-looping;
  2. retry after the active/busy session settles;
  3. dead-letter after a poison retry threshold if the same error signature repeats;
  4. allow later updates on unrelated lanes to continue, and preserve same-DM ordering without hammering the gateway.

Actual behavior

One pending Telegram DM update was retried hundreds/thousands of times, repeatedly failing with the same reply-session initialization conflict. Later DM updates stayed pending behind it.

Operational impact

  • Gateway /health remains live, so basic monitoring reports healthy.
  • Telegram direct-message UX is broken: messages appear ignored because inbound processing is blocked behind the poisoned update.
  • Logs are polluted by repeated retry lines.
  • The retry loop continues without operator intervention.

Useful diagnostic queries

These were useful to identify the stuck state:

select queue_name,event_id,channel_id,account_id,status,lane_key,received_at,updated_at,attempts,last_attempt_at,substr(last_error,1,220) last_error,failed_reason,failed_at,completed_at
from channel_ingress_events
where payload_json like '%<telegram-user-id>%'
order by updated_at desc;
select event_id,status,attempts,
  json_extract(payload_json,'$.update.update_id') as update_id,
  json_extract(payload_json,'$.update.message.message_id') as message_id,
  json_extract(payload_json,'$.update.message.chat.id') as chat_id,
  json_extract(payload_json,'$.update.message.from.id') as from_id
from channel_ingress_events
where status = 'pending';

Workaround

I did not apply a destructive workaround while collecting this report. The likely temporary unblock would be to dead-letter or remove the specific poisoned channel_ingress_events row, then restart or allow the queue to drain, but that would drop the affected user message.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:crash-loopCrash, hang, restart loop, or process-level availability failure.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions