Skip to content

[Bug]: Stalled direct chat model call surfaces LLM request failed instead of auto-resetting/retrying wedged session #90516

Description

@aneym

[Bug]: Stalled direct chat model call surfaces "LLM request failed" instead of auto-resetting/retrying the wedged session

Bug type

Behavior bug (incorrect output/state without crash)

Summary

A Telegram direct chat session with a large/previously compacted transcript wedged during the assistant/model stage; OpenClaw detected active_work_without_progress, aborted the embedded run after ~383s, and delivered a generic user-visible LLM request failed. instead of automatically resetting the hot session state and retrying from preserved durable memory.

This appears related to, but not fully covered by, closed issue #20910 ("Auto-reset session when all models time out (session bloat death spiral)"). In this incident the provider request did not behave like a normal timeout/fallback case: the Responses stream ended after ~383s with zero events/content blocks and Request was aborted, OpenClaw classified the failure as unclassified, no fallback was configured/used, and the direct chat required a manual sessions.reset to recover.

Impact and severity

  • Affected surface: Telegram direct bot account routed to an OpenClaw agent.
  • Severity: high for chat use. The bot is reachable and receives user messages, but the user sees repeated LLM request failed. instead of the tutoring/assistant reply.
  • Frequency in this session: occurred repeatedly for the same direct chat until manual session reset.
  • Practical consequence: the human user kept messaging the bot, saw generic failures, and missed the intended response until an operator manually diagnosed and reset the exact session key.
  • Risk: any long-lived channel session with substantial transcript/compaction history can enter this "gateway and channel are healthy, hot session is wedged" state. The current recovery path correctly detects and aborts, but does not perform the user-preserving automatic recovery step.

Environment

  • OpenClaw version: OpenClaw 2026.6.1 (2e08f0f)
  • Update status: stable channel, no registry/git update available at time of incident.
  • Install method: npm/Homebrew-managed package, LaunchAgent service.
  • Package root: /opt/homebrew/lib/node_modules/openclaw
  • CLI entrypoint: /opt/homebrew/bin/openclaw
  • Service: LaunchAgent ai.openclaw.gateway
  • Service command:
    • /opt/homebrew/opt/node/bin/node
    • /opt/homebrew/lib/node_modules/openclaw/dist/index.js
    • gateway
    • --port
    • 18789
  • OS: macOS 26.3, build 25D125
  • Kernel: Darwin 25.3.0 arm64
  • Gateway bind: 0.0.0.0:18789
  • Effective agent provider/model: codex-lb/gpt-5.5
  • API path logged by OpenClaw: openai-responses
  • Channel: Telegram, polling mode

Steps to reproduce

This is not yet a minimal synthetic repro, but this is the end-to-end incident path observed on a real installed Gateway:

  1. Run OpenClaw 2026.6.1 as the installed LaunchAgent gateway.
  2. Configure a Telegram account and route a direct chat to an agent using codex-lb/gpt-5.5.
  3. Let that direct session accumulate a large transcript and multiple compaction checkpoints.
    • The affected entry had compactionCount: 23 after reset.
    • The reset output showed a prior compaction checkpoint preserving a long Russian review session summary.
  4. Receive a normal Telegram direct user message in that session.
  5. Observe that OpenClaw routes it correctly and starts an embedded agent/model run.
  6. The model call produces no useful content for several minutes.
  7. OpenClaw's stalled-session recovery aborts the embedded run.
  8. OpenClaw delivers generic text LLM request failed. to the Telegram user.
  9. Further messages in the same direct chat continue failing/stalling until an operator manually resets the exact session key:
openclaw gateway call sessions.reset \
  --params '{"key":"agent:<agent-id>:telegram:direct:<redacted-chat-id>","reason":"reset"}' \
  --json \
  --timeout 60000
  1. After reset, a non-delivered probe against the exact same session key succeeds in ~2.3s, and a delivered Telegram reply succeeds in ~3.3s.

Expected behavior

When a long-lived channel session is classified as stalled due to active_work_without_progress and recovery aborts the embedded run:

  1. OpenClaw should recognize the failure as a wedged hot session / compaction or transcript state problem when the gateway, channel, routing, and provider are otherwise reachable.
  2. It should preserve durable agent memory/checkpoints, reset the active session file/key to a fresh safe state, and retry the user's turn once.
  3. It should only surface a user-visible failure after the reset+retry also fails.
  4. The user-facing message should avoid a raw/generic LLM request failed. when OpenClaw has enough context to perform a known recovery.
  5. Operator diagnostics should clearly say something like session reset/retry attempted after stalled model call and include whether the retry succeeded.

Actual behavior

OpenClaw did detect the session stall and abort the embedded run, but then surfaced the failure directly to the user. No automatic reset/retry was attempted.

The manual recovery that made the session healthy again was exactly:

openclaw gateway call sessions.reset \
  --params '{"key":"agent:<agent-id>:telegram:direct:<redacted-chat-id>","reason":"reset"}' \
  --json \
  --timeout 60000

After that, the same direct session key responded normally and delivery succeeded.

Evidence

Gateway and channel were healthy

At diagnosis time the Gateway was up, the installed service was running, RPC worked, and the port listener existed:

openclaw --version
# OpenClaw 2026.6.1 (2e08f0f)

openclaw update status --json
# stable channel, latestVersion 2026.6.1, no update available

openclaw gateway status --require-rpc --json
# cli.version: 2026.6.1
# service.loaded: true
# service.runtime.status: running
# service.runtime.state: active
# gateway.bindHost: 0.0.0.0
# gateway.port: 18789
# rpc.ok: true
# rpc.capability: admin_capable

lsof -nP -iTCP:18789 -sTCP:LISTEN
# node ... TCP *:18789 (LISTEN)

curl -fsS --max-time 5 http://127.0.0.1:18789/health
# {"ok":true,"status":"live"}

Telegram was connected and polling. The affected account showed running:true, connected:true, lastError:null, and recent inbound/outbound activity.

Stalled-session detection occurred

OpenClaw diagnostic events showed an active model call making no progress:

session.stalled
outcome=processing
reason=active_work_without_progress
queueDepth=1
activeWorkKind=model_call

The log line for the affected direct session:

stalled session:
sessionKey=agent:<agent-id>:telegram:direct:<redacted-chat-id>
state=processing
age=353s
queueDepth=1
reason=active_work_without_progress
classification=stalled_agent_run
activeWorkKind=model_call
lastProgress=model_call:stream_progress
lastProgressAge=353s
recovery=none

Then:

stalled session:
state=processing
age=383s
reason=active_work_without_progress
classification=stalled_agent_run
activeWorkKind=model_call
lastProgress=model_call:stream_progress
lastProgressAge=383s
recovery=checking

Provider stream ended with no usable content, then abort was surfaced

[responses] stream_done
provider=codex-lb
api=openai-responses
model=gpt-5.5
elapsedMs=382908
events=0
types=
stopReason=stop
contentBlocks=0

Immediately after:

[responses] error
provider=codex-lb
api=openai-responses
model=gpt-5.5
name=Error
status=undefined
code=undefined
type=undefined
message=Request was aborted

Then:

stuck session recovery:
sessionKey=agent:<agent-id>:telegram:direct:<redacted-chat-id>
age=383s
action=abort_embedded_run
aborted=true
drained=true
released=0

Failover decision:

embedded_run_failover_decision
stage=assistant
decision=surface_error
provider=codex-lb
model=gpt-5.5
sourceProvider=codex-lb
sourceModel=gpt-5.5
fallbackConfigured=false
timedOut=false
aborted=true
rawErrorPreview="Request was aborted"
providerRuntimeFailureKind=unclassified

OpenClaw then delivered the failure text successfully over Telegram:

telegram outbound send ok
accountId=<telegram-account>
chatId=<redacted-chat-id>
operation=sendMessage
deliveryKind=text
chunkCount=1

message dispatch completed
outcome=completed
duration=385848ms

The Telegram user-visible message was:

LLM request failed.

Manual reset fixed the exact session

Manual reset result:

{
  "ok": true,
  "key": "agent:<agent-id>:telegram:direct:<redacted-chat-id>",
  "entry": {
    "sessionId": "975ec9f4-b6fd-44a7-8171-4308c2a91005",
    "sessionFile": "/Users/<user>/.openclaw/agents/<agent-id>/sessions/2026-06-03T14-34-47-669Z_975ec9f4-b6fd-44a7-8171-4308c2a91005.jsonl",
    "systemSent": false,
    "abortedLastRun": false,
    "chatType": "direct",
    "model": "gpt-5.5",
    "modelProvider": "codex-lb",
    "compactionCount": 23,
    "origin": {
      "provider": "telegram",
      "surface": "telegram",
      "chatType": "direct",
      "accountId": "<telegram-account>"
    },
    "deliveryContext": {
      "channel": "telegram",
      "accountId": "<telegram-account>"
    }
  }
}

After reset, a non-delivered probe against the exact direct session key succeeded:

openclaw agent \
  --agent <agent-id> \
  --session-key agent:<agent-id>:telegram:direct:<redacted-chat-id> \
  --message 'Diagnostic only after reset. Reply exactly: TINA_SESSION_OK' \
  --json \
  --timeout 60

Result:

{
  "status": "ok",
  "summary": "completed",
  "result": {
    "payloads": [
      {
        "text": "TINA_SESSION_OK"
      }
    ],
    "meta": {
      "durationMs": 2329,
      "agentMeta": {
        "provider": "codex-lb",
        "model": "gpt-5.5",
        "usage": {
          "input": 7239,
          "output": 8,
          "total": 7247
        }
      },
      "executionTrace": {
        "winnerProvider": "codex-lb",
        "winnerModel": "gpt-5.5",
        "attempts": [
          {
            "provider": "codex-lb",
            "model": "gpt-5.5",
            "result": "success",
            "stage": "assistant"
          }
        ],
        "fallbackUsed": false,
        "runner": "embedded"
      }
    }
  }
}

A delivered reply then succeeded:

{
  "status": "ok",
  "summary": "completed",
  "result": {
    "deliverySucceeded": true,
    "deliveryStatus": {
      "requested": true,
      "attempted": true,
      "status": "sent",
      "succeeded": true,
      "resultCount": 1
    }
  }
}

Why this looks like an OpenClaw bug

This was not a Telegram availability failure:

  • Telegram channel/account was running and connected.
  • Inbound messages were received.
  • Routing matched the intended agent.
  • Outbound Telegram delivery worked, including the bad LLM request failed. message and the later successful reply.

This was not a Gateway-down failure:

  • Gateway health returned live.
  • LaunchAgent service was running.
  • RPC was OK.
  • Port 18789 was listening.

This was not a permanent provider outage:

  • A fresh diagnostic session using the same agent/provider/model returned OPENCLAW_OK in ~1.9s.
  • The exact affected direct session returned TINA_SESSION_OK in ~2.3s after sessions.reset.
  • A delivered reply using the exact affected direct session succeeded in ~3.3s after reset.

The defect is the recovery policy between:

  1. Detecting a stalled direct session,
  2. Aborting the embedded run,
  3. Classifying the runtime failure,
  4. Choosing surface_error,
  5. Sending LLM request failed. to the human,
  6. Not performing the same sessions.reset recovery that immediately fixed the session when run manually.

Proposed fix

Add an automatic safe recovery path for channel-backed direct sessions when all of the following are true:

  • session is processing;
  • reason/classification is active_work_without_progress / stalled_agent_run;
  • active work is model_call or embedded assistant run;
  • recovery aborts the run successfully;
  • provider result is aborted/unclassified/no-content, not a user/tool validation error;
  • the channel account is healthy enough to deliver;
  • the session has compaction history or active transcript size triggers indicating a likely hot-session/transcript wedge.

Suggested behavior:

  1. Mark the failed run as aborted.
  2. Preserve durable memory/checkpoints.
  3. Reset the hot session entry using the same supported mechanism as sessions.reset.
  4. Retry the user's original turn exactly once.
  5. If retry succeeds, deliver the real reply and log auto_session_reset_retry_succeeded.
  6. If retry fails, surface a clearer message and log auto_session_reset_retry_failed with the failed reason.

Guardrails:

  • Do not reset indefinitely. One auto-reset retry per incoming message/session failure is enough.
  • Do not reset on tool permission/validation errors or explicit provider auth errors.
  • Keep the original inbound message id/trace linked to the retry for operator audit.
  • Make the diagnostic/event stream show that an auto reset was attempted.

Temporary workaround

Manual operator reset of the affected direct session:

openclaw gateway call sessions.reset \
  --params '{"key":"agent:<agent-id>:telegram:direct:<redacted-chat-id>","reason":"reset"}' \
  --json \
  --timeout 60000

Then verify without delivering:

openclaw agent \
  --agent <agent-id> \
  --session-key agent:<agent-id>:telegram:direct:<redacted-chat-id> \
  --message 'Diagnostic only after reset. Reply exactly: OK' \
  --json \
  --timeout 60

Additional notes

  • The first attempted sessions.reset with a descriptive reason failed schema validation because reason appears to require a strict constant. That is probably fine, but a clearer CLI error or accepted enum in help would make operator recovery easier.
  • The installed recovery runbook expected this exact manual reset shape for "large transcript failures can show up as preflight compaction stuck near maxActiveTranscriptBytes"; this incident confirms the runbook but suggests Gateway should perform the safe path automatically for channel sessions.
  • Public issue is redacted: chat id, account names, local username, and human names have been omitted or generalized.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.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: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