Replies: 3 comments
修复已完成(补丁已就绪)核心修复已实现并测试,分支已推到 fork: fork 分支: yeruizhi/deepseek-harness@fix/ask-user-question-timeout 改动(6 文件, +95/-10):
说明: 官方仓库当前禁用了 issues 和 pull requests(与 CONTRIBUTING 声明一致),无法直接提交 PR;若未来开放 PR 通道,此分支可直接提交。附带独立可装的 guard 插件 yeruizhi/dsh-ask-user-timeout 实现了同样语义,可立即使用。 |
|
Confirming this still reproduces on Adding one thing your event-log analysis can't show, in case it helps whoever picks this up:
That's also why it's so expensive in practice — there is no signal at all. No error at any log Interim mitigation for anyone else bitten by this, until there's a timeout: a 2-minute watchdog Strong +1 on the timeout in #2544's suggestion 1. Worth noting the same no-timeout pattern shows up |
|
Thanks @TerrysPOV — this is exactly the kind of follow-up that turns a bug report into something actionable, and your SIGUSR2 diff shows the half my event-log analysis never could. The "bare promise with no I/O behind it" point deserves emphasis for whoever picks this up: it means detection cannot happen at the runtime level — every conventional hang triage comes back clean, as you demonstrated — so the only correct place to bound this is the protocol layer, i.e. a deadline on the pending question itself. Which also explains why the fix is small: there is nothing to unwind, just a timer that never existed. Your watchdog write-up is a genuinely useful interim mitigation — catching a stalled turn at the 10-minute mark instead of the next morning is the difference between an overnight run that finishes and one that burns a day. For anyone who wants bounded-wait behavior today: dsh-ask-user-timeout implements the same semantics as an installable plugin — it wraps +1 on reading #4926 / #3588 together with this one as a missing convention — "anything that waits on a connected client needs a bound" feels like it belongs in the plugin authoring guide, not scattered across three threads. |
Uh oh!
There was an error while loading. Please reload this page.
Bug:
ask_user_questionhangs indefinitely when no browser tab renders the question — no timeout, no notification, only manual cancel returnsASK_ABORTEDSummary
When
ask_user_questionis called while the owning Web UI tab is not actively rendering the question (page closed, tab backgrounded overnight, mux connection dropped), the tool call hangs indefinitely with no timeout, no re-notification, and no visible error. The UI keeps showing "running in progress" for the whole time. The only way out is for the user to manually press the stop button, which then surfaces a misleadingASK_ABORTEDerror ("ask_user_question was aborted before the user answered") — implying an abort happened by itself, when in fact the question was never rendered to anyone.Evidence (from a real session event log)
Two
ask_user_questioncalls in the same session, same code path:For call #2:
tool/callseq 126534 at 23:58:27 withcallId; after that there is notool/result, nostep/end, noturn/endfor over 8 hours — the loop stays "running".tool/resultseq 126535 at 08:18:03 (immediately after the user clicked the stop button) with content:Error: ask_user_question was aborted before the user answeredquestion/resolved, noagent/cancel-requested-attributed event, no timeout event precedes it. The abort came from the user's manual stop.Root cause analysis
1. The question is rendered only by the live browser composer takeover
The browser half mounts
QuestionComposervia a slot injected into the conversation composer chain:packages/client/ui-user-questions/src/client/index.ts—ctx.slots.inject('conversation.composer', ...)with selectorselectQuestionmatchinginteractions.find(i => i.kind === 'question').question/requestedmux frame:packages/client/runtime/src/client/sessions/session.ts:500→new PendingWait('question', ...).If no browser tab is connected to that session's mux, or the tab is not rendering the composer chain (closed / backgrounded / disconnected), nobody ever sees the question — the host-side pending entry exists, but there is no viewer.
2. Re-delivery only happens on mux (re)connect — not on a live-but-inactive tab
On a fresh mux subscription the proxy replays pending questions (
packages/host/apiproxy/src/api-proxy.ts:3436). But a tab that stays open and connected while inactive does not re-render the question — the frame was already delivered once and consumed into aPendingWaitthat nothing displays. There is no heartbeat / re-ping / re-claim.3. No timeout or escalation exists anywhere in the pending-question lifecycle
packages/client/runtime/src/client/sessions/pending.tsandsession.tscontain no timeout/expiry forPendingWait.packages/host/apiproxy/src/api-proxy.ts:1376keeps thependingQuestionsentry until aborted; nothing expires it.packages/interaction/user-questions/src/index.ts) has no deadline either —ask()blocks onthis.provider.ask(request).4. The stop button produces a misleading error
The user's manual stop runs
agent.cancel({ kind: 'user' })(api-proxy.ts:2631), which aborts the turn signal; the pendingaskthen throwsASK_ABORTED(user-questions/src/index.ts:93-95;api-proxy.ts:1382-1386). The UI comment inpackages/client/ui-tool/src/client/tool/toolviews/ask-question-row.tsx:51literally labelsASK_ABORTEDas "a turn interrupt landing while the question was pending" — so the error is correct as an abort report, but it is triggered by the user's own manual cancel, not by anything that happened with the question itself. From the user's perspective the session showed "running" for 8+ hours with no question visible and no error, and the eventual message reads like an internal abort rather than "you had to cancel a hung question".Suggested fixes (open to maintainers)
PendingWait('question')(and the hostpendingQuestionsentry) a bounded lifetime (e.g. 5–10 min). On expiry, surface a visible "question went unanswered / re-ask" state in the UI and/or let the model know the ask expired, instead of hanging forever.question/requestedon reconnect — and optionally onvisibilitychange/focus so a backgrounded tab re-shows the question when the user returns.ASK_CANCELLED) from "turn interrupted" (ASK_ABORTED) in the summary the user sees, so a manual stop of a never-visible question reads as cancelled, not aborted.Environment
deepseek-ai/deepseek-harness47f943859b(feat/npm-publicmerge)dsh web), standard agent presetSteps to reproduce
dsh web, open a session.ask_user_question(e.g. a choice between options).ASK_ABORTED.All reactions