[BUG] Claude Code on the web: an idle session appears to be periodically re-woken on the backend, re-emitting its pending plan/question prompt each time — so on return the prompt is stacked N times, answers are silently lost, and the count scales with idle duration

Status Open
Maintainer reply None cached
Activity 7 comments · opened Jul 1, 2026

Environment

  • Surface: Claude Code on the web (claude.ai/code, browser)
  • Mode: Plan mode (and, separately, the AskUserQuestion / clarifying-question UX)
  • Trigger condition: the session has been idle for a while — long enough that clicking into it shows a "Resuming session…" step before it becomes interactive. This does not reproduce on a session that's been continuously active.
  • Context: running many (10–15) concurrent web sessions, so any individual session sits idle between visits.

What happens

I prepare a plan in plan mode, then leave the session to tend to other sessions. When I come back later and click Accept on the already-prepared plan:

  1. The app shows "Resuming session…" and processes for a while.
  2. Instead of exiting plan mode and starting implementation, it re-presents the same plan for approval.
  3. Accepting again repeats the cycle — sometimes several times in a row.

Workarounds are unreliable. Sometimes if I just keep clicking through — accepting again and again, each time waiting ~a minute for the backend to churn — it eventually proceeds. Other times I have to reject the plan, exit plan mode, and manually type "go ahead" as a normal message. Either way it's uncomfortable at scale: with many concurrent sessions I keep returning to the same stuck one, and a single task can stall for hours.

The worst part: reviewed plans and detailed answers get permanently lost

The wait is annoying, but the real damage is data loss. Time spent reviewing a plan, or writing detailed answers to clarifying questions, sometimes just disappears — the response never takes effect and there's no way to get it back. I've had to start writing all my answers in a separate Notes app first, so I can copy-paste them back in after the prompt inevitably reappears. Losing structured, carefully-written input silently is a much bigger problem than the latency.

The same thing happens to the question-asking UX — and how it re-appears points at the cause

The identical pattern affects clarifying questions (AskUserQuestion). After coming back, the session re-asks the same question(s) I already answered — and here it's more revealing in two ways:

  1. The phrasing is slightly different each time. Same underlying question, re-worded. That points to the agent genuinely re-deriving the prompt from scratch each time (a fresh, nondeterministic model turn), not the UI re-displaying one cached prompt.
  1. The re-asks keep coming even while I stay on the page. I return, see a question, answer it. About a minute later I see the same question re-phrased — answer again. A minute after that, another rephrasing — and so on. Since I never left the page after the first answer, a plain "re-run on user revisit" explanation doesn't fit: something is re-triggering the turn without my interaction.
  1. The number of repeats scales with idle time. Come back within an hour and it might re-show once. Start in the morning and return in the evening and it can re-show 3–4 times.

Putting those together, my best guess at the cause: something on the backend is periodically "resurrecting" / re-invoking the idle session on its own (a keepalive, heartbeat, or scheduler waking it), and each wake re-executes the pending turn and re-emits the interactive prompt — with no user on the receiving end. Those unanswered re-emissions appear to accumulate (roughly one per wake, hence proportional to idle duration), so when I finally return I have a backlog of stacked prompts to clear one at a time, and my earlier answers — given to prompt instances that were subsequently superseded — are dropped. That would explain all four observations: the re-wording, the count scaling with idle time, the re-asks continuing while I sit on the page, and the silent loss of answers I already gave.

Steps to reproduce

  1. On Claude Code web, start a session and get the agent to a pending interactive prompt — a plan awaiting approval in plan mode, or a clarifying question via AskUserQuestion.
  2. Leave the session idle for a long stretch (working across many concurrent sessions makes this natural). Longer idle = more repeats, so an all-day gap shows the effect most clearly.
  3. Return and answer the prompt (Accept the plan / answer the question).
  4. Observe: after ~a minute of backend churn the same prompt re-appears — a plan re-presented identically, or a question re-asked with slightly different wording — even though you never left the page.
  5. Answer again; it can re-appear again. The number of times roughly tracks how long the session sat idle (≈1 within an hour, 3–4 over a full day).
  6. Reviewed plans / detailed answers given to the superseded prompt instances are lost, not applied.

Expected behavior

  • An idle session should not re-execute its pending turn on its own. If a keepalive/heartbeat touches an idle session, it must not re-run the model or re-emit the pending interactive prompt.
  • When the user answers a pending plan-approval / question, that response should be consumed exactly once and the session should proceed — exit plan mode into implementation, or continue with the answered question.
  • A user's reviewed plan approval and written answers must never be silently discarded.

Actual behavior

  • The pending interactive prompt is re-emitted repeatedly, apparently once per backend re-wake, with the count proportional to idle duration — even with no user present and even while the user stays on the page after their first answer.
  • Answers to the earlier (superseded) prompt instances are dropped, so carefully-written input is silently lost.
  • Clearing the backlog requires either accepting/answering repeatedly (minutes each) until it finally proceeds, or rejecting → exiting plan mode → typing "go ahead" manually, which discards the structured plan/answer.

Impact

  • Silent data loss of reviewed plans and detailed answers — the most serious part. Workaround is to draft all answers in a separate Notes app and paste them back after each re-ask.
  • Tasks stall for hours across a fleet of concurrent web sessions; both workarounds defeat the purpose of plan mode / structured questions.

Related but distinct issues (checked — none cover this)

  • #34948, #32923, #33479 — plan-approval loops, but terminal/TUI only (area:tui), and none involve the web "Resuming session" idle-resume trigger. All closed/stale.
  • #24048 — web sessions go permanently idle/frozen after approval (a hard freeze). This bug is the opposite: the session does resume and stays responsive; it just discards the response and re-presents.
  • #41128 — idle web/mobile sessions can't be resumed at all. Here resume succeeds; the problem is what happens after resume.

The distinguishing combination — web UI + idle session apparently re-woken on the backend + pending plan/question prompt re-emitted N times proportional to idle duration + silent loss of the user's answers — does not appear to be reported yet.

View original on GitHub ↗

7 Comments

vzakharov · 1 month ago

Follow-up (started appearing recently) — a new variant that may point at the mechanism.

On a resumed idle web session where the pending prompt is an AskUserQuestion, the agent now surfaces a transient stream error and retries the tool itself, narrating it in chat before re-asking — e.g.:

The AskUserQuestion hit the same transient stream error. Let me retry it.

…and then it re-emits the same question. Notably, the re-sent question block still shows the option I'd already chosen, yet the turn proceeds as if it were unanswered, so it asks again (and, as before, can repeat several times).

<img width="444" height="276" alt="Image" src="https://github.com/user-attachments/assets/4cbaca75-c8c7-4cac-b49a-91908b87dea0" />

Why this might shed light: the "transient stream error → retry" narrative suggests the interactive tool round-trip itself is failing (a streaming/transport error on the AskUserQuestion / ExitPlanMode call), and the model's built-in retry-on-error then re-invokes the tool, re-showing the prompt. If so, at least some of the repetition would be model-driven retries after a failed tool call, not purely the UI re-displaying a cached prompt — and it lines up with the original report that answers are silently lost: the user's response never makes it back through the failing stream, the model sees an error instead of an answer, and it asks again.

This may be related-but-distinct from the original symptom (there, no error was ever surfaced — the prompt just silently reappeared). Flagging it in case the transient-stream-error path and the idle-re-wake path share a root cause in how interactive tool responses are streamed back on resume.

vzakharov · 1 month ago

Controlled test, observed from inside a session (corroborates the transient-stream-error path above).

I deliberately triggered the bug and watched it from both ends — the operator's UI and the agent's own tool-result view.

Setup: the agent invoked AskUserQuestion, then the operator left the session idle for ~3 hours, answered, and returned.

Operator side (UI):

  1. The question card appeared normally.
  2. The operator typed a custom (free-text) answer and submitted it.
  3. It did not re-ask this round (see "why" below).

Agent side (from within): the answer never arrived. The AskUserQuestion call resolved as an error, not a response:

Tool permission request failed: Error: Tool permission stream closed before response received

On resume the agent received a bare "Continue from where you left off" with no answer payload attached. The operator's custom answer was confirmed on their end but never reached the agent — it's simply lost.

Why it didn't re-ask this time: the agent happened not to retry the errored tool (it moved on to diagnosing instead). The repeated re-asking others see appears to be the agent retrying the failed interactive tool call — a model seeing a tool error naturally re-invokes it, re-emitting the prompt. So the re-ask is agent-retry behavior on top of the real bug; the answer loss happens regardless of whether the prompt visibly re-appears.

Timeline (authoritative server timestamps):

| Event | Time (UTC) |
|---|---|
| AskUserQuestion invoked (operator went idle shortly after) | ~17:14Z |
| Operator active in-session (unrelated edit) | 17:48Z |
| Resume — error + "continue" surfaced to the agent | ~20:12Z |

Idle window ≈ 3 hours, consistent with the earlier observation that repeat-count scales with idle duration.

**Caveat on when the stream closed:** from the agent's vantage the error and the resume are contemporaneous — it only sees the failed tool result at the instant it's re-invoked. It can't distinguish "the permission stream closed early in the idle window" from "it closed at the moment of resume." Server/harness logs would show which; the transcript doesn't timestamp it.

Net: the interactive tool's response channel (the "permission stream") does not survive an idle→resume cycle. The pending call resolves as a stream error, the operator's answer is dropped, and any visible re-asking is the agent retrying the errored call.

rpelevin · 1 month ago

The controlled test narrows this to a response-channel authority problem, not just a duplicated prompt rendering problem.

I would model the pending interaction as a single outstanding approval/question envelope with four separate states:

  1. prompt emitted to operator
  2. operator response accepted by UI
  3. response delivered to the waiting agent turn
  4. pending tool call consumed or failed with a terminal no-effect result

The bad state here is that step 2 can succeed while step 3 resolves as stream closed before response received. After that, retrying AskUserQuestion creates a new prompt, but the system has already lost the operator answer. That is why the visible re-ask is a symptom; the actual invariant break is accepting an answer without binding it to the original pending call.

A fix/test I would want:

  • give every pending interactive tool call a stable interaction_ref that survives idle resume
  • persist the operator response against that ref before acknowledging it in the UI
  • on resume, deliver that persisted response exactly once to the original waiting call, or surface a terminal failed-to-deliver state to both sides
  • reject duplicate/re-derived prompts while an answered interaction_ref is still unresolved
  • add a regression test where the permission stream closes after UI acceptance and before agent delivery

The critical assertion is: a user answer is not "accepted" until it is durably joined to the pending tool call, or the UI clearly says the answer was not delivered. Anything else can silently convert human approval into data loss.

vzakharov · 1 month ago

Building on @rpelevin's response-channel framing — that's exactly the right model, and a controlled test plus a few more live samples from our side line up precisely with the "state 2 succeeds, state 3 fails" split, and add the missing trigger.

Three delivered-vs-lost samples, one live session:

| Interactive answer | Timing | Delivered to the agent? |
|---|---|---|
| custom free-text | after idle | ❌ lost — agent received stream closed before response received |
| discrete option-click | after ~45 min idle | ❌ lost |
| custom free-text | answered promptly, session still live | ✅ delivered |

Two details sharpen it:

  • The dropped option-click happened while an ordinary chat message the operator sent seconds later arrived fine. So the break is specific to the interactive-answer channel, not general message transport — a clean instance of "UI accepts the answer (state 2) but delivery to the waiting call (state 3) resolves as stream-closed."
  • The only delivery that succeeded was answered promptly, before the session went idle. Idle is the discriminator: the answer channel does not survive an idle→resume cycle, even though the prompt still renders on the operator's side.

The trigger (what re-executes the turn): it appears to be opening/focusing the session tab, not an autonomous timer. Observed directly: the operator clicked into the idle session, saw "Resuming session," and clicked away without answering — and that alone resumed the turn and re-surfaced the pending call as the same stream error. This reframes the "repeats scale with idle time" report — it really scales with the number of times the session is viewed during idle. When juggling many concurrent sessions, longer idle ⇒ more incidental visits to this one ⇒ more re-executions, and each view re-emits the prompt.

So the visible re-ask is a symptom on two levels: (a) the agent may retry the errored call (a short burst within one resume), and (b) each subsequent view re-executes the whole turn (more prompts, each a fresh generation — hence the slight re-wording operators notice).

Why @rpelevin's proposed fix closes both: a stable interaction_ref that survives resume, plus "reject duplicate/re-derived prompts while an answered ref is unresolved," would neutralize both paths — a view-triggered re-execution or an agent retry would hit an already-answered (or terminally-failed) ref instead of spawning a fresh prompt. And "persist the operator response against that ref before acknowledging it in the UI" is what stops an accepted answer from being silently converted into data loss.

Net model: view/resume re-executes the pending turn → no durable ref binds the answer the operator already gave → the answer is lost (state 3 fails) and the prompt is re-emitted. Trigger = viewing the session; invariant break = an answer accepted-but-unbound; visible re-ask = a symptom of both.

rpelevin · 1 month ago

Thanks, this is a useful narrowing.

The important new discriminator is that ordinary chat delivery can still work while the interactive-answer channel fails after idle/resume. That makes the bug less like general transport loss and more like a pending-interaction binding failure.

I would treat tab focus / session resume as a new execution attempt against the same pending interaction_ref, not permission to mint a fresh prompt.

The regression I would want is:

  1. create pending interaction_ref
  2. let the session go idle
  3. focus/resume the session
  4. submit an option or free-text answer
  5. assert the response is durably bound before UI acknowledgement
  6. assert any re-render, retry, or resumed turn reuses the same ref or returns a terminal already_answered / delivery_failed state, rather than emitting a fresh prompt

That keeps the UI state, durable answer record, and waiting runtime call in one authority chain.

The key failure to prevent is accepted-but-unbound input: the operator sees a successful answer, but the runtime resumes as if no answer exists.

vzakharov · 1 month ago

Capstone: a 33-hour single-view test rules out an autonomous timer — the multiplicity is view-triggered.

Setup: armed a pending AskUserQuestion, left the session completely untouched for ~33 h (no views at all), then opened it exactly once.

Result:

  • Exactly one prompt on that single open — not a stack. An autonomous timer / periodic re-emit would have accumulated copies over 33 h regardless of views; it did not.
  • Answering it (custom field) on that open was lost: the agent side had long since resolved the call as stream closed before response received, and opening the session is itself the resume that finds the call already failed — so the answer had no live call to bind to. (Same state 2 accepted / state 3 fails split as above.)

Conclusion: the in-the-wild "same prompt 3–4×" is (number of times the session is viewed) × (1 + agent retries)not a clock. Each view/open is a resume that re-executes the pending turn and re-emits the prompt; the more a session is revisited during idle (natural when juggling many concurrent sessions), the more copies stack. This corrects an earlier guess in this thread about a periodic backend re-wake — there is no timer; it's re-execution on resume.

Implication for the interaction_ref fix: the guard that "rejects duplicate/re-derived prompts while an answered ref is unresolved" needs the ref and the persisted response to survive resume, because resume/view is exactly what re-executes the turn. There's no timer to suppress — only re-execution to make idempotent, and an accepted answer to persist before the resume can discard it.

CoderNumber1 · 1 month ago

Hitting what looks like the same root cause on a different surface — the Claude Android app (Claude Code session), 2026-07-31, model claude-opus-4-8.

Environment: Claude Android app v1.260721.20 (Google Play Store), Android 16.

A few differences from the original report that may help localize it:

  • Surface: Android Claude app, not browser web.
  • Faster cadence + focus theft: the already-answered AskUserQuestion widget re-pops roughly every 1–2 seconds and grabs keyboard focus from the message composer each time — typing was nearly impossible, not just a per-minute re-ask.
  • Answers weren't lost here: subsequent turns correctly had my answers server-side, yet the widget kept re-rendering the resolved questions — so on this surface it presents as a stuck / re-emitted client widget over already-consumed answers, rather than silent answer loss.
  • Active session, not idle: this happened mid-session while actively working, not only after a long idle gap.
  • Survives a full restart: force-closing the app, clearing app cache, and reopening the session did not clear it.
  • Eventually self-cleared: after several more turns of normal back-and-forth (no special action), the stuck widget stopped re-popping on its own — consistent with the "re-emitted pending prompt" being superseded/consumed rather than permanently stuck.

Had to hand the work off to a separate session while it was happening.