Repository navigation
Description
Area
[GUI] (the desktop app)
Desktop app, Terminal or source?
Desktop app (Windows, Linux or Mac)
What happened
In an agent (coding assistant) chat with a local model, the turn was ended with:
The assistant stopped answering for 4 minutes, so I ended this turn. Anything it already finished is kept. Send your message again to go on.
The model had not stopped. The engine was reading a 59,091-token prompt. Only 6,154 tokens came from the prefix cache, and it had read 52,932 of the remaining 52,937 at 208 tok/s when the turn was cancelled at 255 s:
strata serve: prompt 59091 tokens = 6154 reused + 52932 of 52937 read in 254492 ms (208.0 tok/s), 0 generated in 0 ms (0.0 tok/s), drafts accepted 0 of 0, 4 checkpoints (cancelled)
The request just before it (85,480 tokens: 68,036 reused, 17,444 read in 95 s) finished normally. The engine was healthy. The cache miss is what made this read take longer than 4 minutes.
The app's progress lines for the request (one per 6,144-token chunk, about 29 s each)
[strata] reading the prompt: 59,091 tokens, 10 s so far
[strata] reading the prompt: 12,298 of 59,091 tokens, 28 s so far
...
[strata] reading the prompt: 55,306 of 59,091 tokens, 243 s so far
[strata] done: 0 tokens in 255 s (0.0 tok/s) (cancel, cancel=True)
Expected: while the engine is making progress on the prompt, the turn keeps waiting. It should only be ended when neither the Harness nor the engine has moved for the stall time.
Why it happens (from reading the code)
Two timers can end a healthy request, and neither sees the engine's keep-alives:
-
The agent watchdog hears only the Harness. In
apps/host/server/agent/agent-bridge.mjs,turn.watchdogends the turn whenDate.now() - turn.lastActivity >= stallMs.stallMsis 4 min, and there is no setting or environment variable for it.turn.lastActivityis updated only for Harness items (handle,fold). While the engine reads a prompt it sends only: keep-aliveSSE comments, which the OpenAI client drops. So the Harness stays silent, and any prompt read longer than 4 minutes ends the turn.serve already handles this case:
engine_silence_sis extended by prompt-progress lines (serve engine deadlocks in SleepConditionVariableSRW during a long streaming reply; stall watchdog never fires #481), andSTRATA_ENGINE_STALL_Sonly ends an engine that also uses no CPU, disk or GPU (serve: an untimed read of the engine/vision pipe can hold the request turn forever (intermittent hang; the #481 lost-step class) #1317). The host also already polls the engine's progress every 2 s (/metrics→live.state,live.prompt_read,live.generatedinnative-control.mjs). The watchdog just does not use it. -
The Harness's LLM client has the same blind spot.
@deepseek-ai/dsh-llm-pi-aidrops a request afterstreamIdleTimeoutMs(default 300000) without a stream event. The error ispi-ai stream idle timeout after 300000ms, codeTIMEOUT.openai/core/streaming.jsdrops lines that start with:, so neither the queue beats nor the prompt-progress comments reset that timer.apps/host/server/agent/profile.mjssets no value for local providers. With the defaults the watchdog fires first, so fixing (1) alone only moves the cut-off from 4 to 5 minutes. That is why both are in one report. It is the keep-alive-as-comment problem of serve: keep-alive is an SSE comment, so clients that drop comments still hit their stream idle timeout and retry #1951 / PR serve: the keep-alive while a prompt is read is a real SSE frame, not a comment #1953 (closed, not merged), but for Strata's own agent.In a 3.5 h run with the timeout raised to 15 min (patch below), the two helpers' sessions show 16
llm/retryevents, all withpi-ai stream idle timeout after 900000ms(codeTIMEOUT). Seven of the gaps between one retry and the next are 900–902 s, so the retried request timed out again after exactly 15 min. With one slot, a helper's request first waits behind the other agents' turns and then reads its own prompt, so it hears nothing for more than 15 min. For 15 of the 16 retries, the engine log shows the dropped request as(cancelled)with 0 tokens generated, 153 to 752 s into the read. Each was then sent again and read from the start, which took about 1.8 h of the 3.5 h of engine time. A fixed timeout does not fit this case, because the wait grows with the number of agents that share a slot.
Local patch I am running (works for me; not proposed as the fix)
engineProgress(instanceId), passed fromindex.mjs, returns a short fingerprint of the engine'slivemetrics, or null when the engine is idle or its metrics are unavailable.- For local routes, the watchdog counts a changed fingerprint as activity. Because the fingerprint covers the whole engine, it counts only for
engineGraceMs(30 min) after the Harness was last heard.stallMsstays at 4 min, so an idle or stuck engine still ends the turn as before. A lost turn ends at about 34 min, and API routes are unchanged. - Local providers get
streamIdleTimeoutMs: 60 * 60_000as a backstop. 15 min was not enough once helpers shared the slot. A hung engine is still ended by serve, and a lost turn by the watchdog cap above. - A real fix would let pi-ai count the engine's keep-alives (or its
return_progresschunks) as activity, and leave hang detection to serve (serve engine deadlocks in SleepConditionVariableSRW during a long streaming reply; stall watchdog never fires #481, serve: an untimed read of the engine/vision pipe can hold the request turn forever (intermittent hang; the #481 lost-step class) #1317).
Diff (hand-trimmed to the changed lines; not meant for git apply)
diff --git a/apps/host/server/agent/agent-bridge.mjs b/apps/host/server/agent/agent-bridge.mjs
--- a/apps/host/server/agent/agent-bridge.mjs
+++ b/apps/host/server/agent/agent-bridge.mjs
@@ export function createAgentBridge(...) @@
-export function createAgentBridge({ ..., expertSampling = () => null, stallMs = 4 * 60_000, stallToolMs = 20 * 60_000, ... }) {
+export function createAgentBridge({ ..., expertSampling = () => null, engineProgress = () => null, engineGraceMs = 30 * 60_000, stallMs = 4 * 60_000, stallToolMs = 20 * 60_000, ... }) {
@@ turn.watchdog = setInterval(() => { ... @@
const limit = toolRunning ? stallToolMs : stallMs;
+ // A local engine reading a long prompt sends the Harness nothing for minutes. Its own progress
+ // (engineProgress: the host's /metrics poll) counts as activity, so only an engine that is idle or stuck lets the turn stall.
+ // It counts for at most engineGraceMs after the Harness was last heard: progress for another chat cannot keep a lost turn forever.
+ if (route.kind === 'local') {
+ if (turn.lastActivity !== turn.engineBump) turn.heard = turn.lastActivity; // the Harness moved it since the engine last did
+ let mark = null;
+ try { mark = engineProgress(route.instanceId); } catch { /* no progress known: judged by the Harness alone */ }
+ if (mark != null && mark !== turn.engineMark && Date.now() - turn.heard < engineGraceMs) {
+ turn.engineMark = mark; turn.lastActivity = turn.engineBump = Date.now(); return;
+ }
+ }
if (Date.now() - turn.lastActivity < limit) return;
diff --git a/apps/host/server/index.mjs b/apps/host/server/index.mjs
--- a/apps/host/server/index.mjs
+++ b/apps/host/server/index.mjs
@@ const agent = createAgentBridge({ @@
expertSampling: () => samplingFromExpert(store.get().expert),
+ // How far a local engine has got (the 2 s /metrics poll), for the agent's stall watchdog. It moves while
+ // a long prompt is read, when the agent hears nothing; null when the engine is idle or says nothing.
+ engineProgress: instanceId => {
+ const live = native.readyInstances().find(i => i.id === instanceId)?.metrics?.live;
+ if (!live || live.state === 'idle' || live.state === 'unloaded') return null;
+ const slots = Array.isArray(live.slots) ? live.slots.map(s => `${s?.state}:${s?.prompt_read ?? ''}:${s?.generated ?? ''}`).join(',') : '';
+ return `${live.state}|${live.prompt_read ?? ''}|${live.generated ?? ''}|${slots}`;
+ },
diff --git a/apps/host/server/agent/profile.mjs b/apps/host/server/agent/profile.mjs
--- a/apps/host/server/agent/profile.mjs
+++ b/apps/host/server/agent/profile.mjs
@@ local.forEach((inst, index) => { ... providers[route] = { @@
baseURL: inst.baseUrl,
apiKeyEnv: keyEnv,
+ // A long prompt read sends only SSE comments, which do not reset the 300 s default idle timer.
+ // 60 min: with helpers taking turns on one slot, a request waits for the others and then reads its own prompt (15 min was
+ // exceeded 16 times in one run); a hung engine is ended by serve itself, and a lost turn by the bridge's watchdog.
+ streamIdleTimeoutMs: 60 * 60_000,Validation (the patch applied to the installed 0.2.1 files on this PC)
Passed:
node --checkwith Strata's bundled Node (v24.12.0) onhost/server/agent/agent-bridge.mjs,host/server/index.mjsandhost/server/agent/profile.mjs: exit 0.buildProfile()from the patchedprofile.mjs, called from a small script with one local instance:streamIdleTimeoutMs: 3600000onstrata-localand on all fourstrata-local~sNroutes.- A script that takes the patched watchdog block and
engineProgressfrom the installed files and runs them on a simulated clock: 11 of 11 cases pass (list below). - In the app, the 3.5 h agent run above (one parent and two helpers on this one-slot engine, watchdog patch with a 15 min
streamIdleTimeoutMs): 23 prompt reads took 240 s or more, 11 of them finished normally (the longest 483 s), and the watchdog did not end the turn once. - In the app, the next run with 60 min (2.7 h of engine time, 52 requests): 6 prompt reads took 240 s or more and all finished, with 0 cancelled reads and 0
llm/retry. I turned on the engine's conversation cache (--conversation-cache-mib 4096) at the same time, which also shortens the waits.
Failed:
- 15 min for
streamIdleTimeoutMswas not enough: 16llm/retryin the 3.5 h run, as described in (2).
Not run:
- Strata 0.2.2 / engine 0.1.47, Linux and macOS.
apps/host/tests/agent-watchdog.test.mjs(it needs a built Harness inSTRATA_HARNESS_DIR).- API-model routes in the app (covered only by the simulation).
- The diff against
main. It was made against the installed 0.2.1 files.
The 11 simulated-clock cases
- Do not stall: a 12-minute prompt read followed by generating; batch slots that keep moving.
- Stall at 240 s, as before: a read whose
prompt_readnever moves; a read with no progress line yet; an idle engine; an unloaded engine; missing metrics; an API route. - The cap: an engine that keeps moving while the Harness stays silent stalls at 2025 s (30 min + 4 min). An item from the Harness at 25 min restarts the cap (stall at 3525 s). Harness items every 10 min: no stall.
- Known limit:
prompt_readmoves once per prompt chunk, so a single chunk that takes more than 4 minutes would still end the turn. Here chunks took about 29 s. - A test in the style of
apps/host/tests/agent-watchdog.test.mjscould cover this: a scripted worker that stays silent for longer thanstallMswhileengineProgresskeeps changing should not end the turn.
Smallest steps or command that reproduce it
Seen once. I have not re-run it on purpose yet.
- Desktop app, agent chat with the local model below, in a long session with many tool calls (the prompt had grown to ~59k tokens).
- Branch the chat and send a message in the branch. The prefix cache matched 6,154 tokens, so 52,937 tokens had to be read again at ~208 tok/s (about 254 s).
- At 255 s the turn ended with the message above. The engine log shows the read still progressing until the cancel.
The trigger (a long re-read after a cache miss) looks related to #3493. The failure itself (the agent watchdog) is separate.
Reproduction status
Reproduced locally
Strata version
Strata 0.2.1 (desktop app) · engine 0.1.46.1 (windows-x86_64-hip-rdna3) · DeepSeek Harness (@deepseek-ai/dsh) 0.2.1-alpha.3. I have not run 0.2.2. As of 2026-10-22, main still has stallMs = 4 * 60_000 with turn.lastActivity set only from Harness items, and profile.mjs sets no streamIdleTimeoutMs.
Graphics card, memory and system
AMD Ryzen AI MAX+ 395 with Radeon 8060S (gfx1151, HIP). The GPU is integrated: Windows reports 512 MiB of dedicated GPU memory (the BIOS carve-out), and the engine works from the 128 GB of unified memory (127.1 GiB visible to Windows). Windows 11 (build 26300).
Model, size and settings
Qwen3.8-Flash-Next UD-Q4_K_XL (Unsloth) · 131072 context (--kv int8 --kv-resident 32768) · pictures on · thinking Max (budget 24576 of max_tokens 32768) · parallel 1
Logs
The excerpts are above. Related: #481, #1317, #1619, #1951, PR #1953, #3493.
Investigated and drafted with Claude Code (code reading, log analysis and writing). The numbers and log lines come from the logs on my machine and were cross-checked against them.
Activity
bghyt commentedon Oct 21, 2026
I have the same issue, i gave it a prompt and it was doing work, after 4 mins of thinking it just stopped it and ruined all my progress
wright802 commentedon Oct 22, 2026
I've been having this for a couple of days now as well. v0.2.1 and now v0.2.2, Linux. After the processing stops, in the prompt box, I've had fair success in typing
continue-- about half the time it will pick up where it left off as if nothing happened. But right now it has this issue and it can't continue (I get the same message after another 4 minutes have passed) so I'll start a new session.EDIT: For anyone that might be following this... try switching to lower thinking. I was on High, changed to Medium, typed Continue, and it powered through and kept working my task. I switched back to thinking High after about 5 minutes, I have no idea if that mid-stream change back to High was accepted, token speed never changed much.
I haven't researched why, this analysis by tomamamma seems rigorous, but if you need me to try something, LMK. Exact message pasted below.
The assistant stopped answering for 4 minutes, so I ended this turn. Anything it already finished is kept. Send your message again to go on. Try sending the message again.Penguinehis commentedon Oct 22, 2026
here i needed to roll back to the beta, the V0.2.2 engine stops even on my custom agent making it impossible to continue long sessions
xvcezrqpbxtpzblrt commentedon Oct 22, 2026
Same 4-minute watchdog on 0.2.2, and a restart makes it reproducible every time.
After restarting the desktop app, the existing 315,993-token chat cannot continue. The first message after the restart re-reads the whole prompt with 0 cache reuse; the turn is cancelled at 259 s while the engine is still reading:
Engine log for the same request:
The restart was at 00:29:54, the engine started at 00:31:24, and the request was cancelled at 259 s. The app log is
%LOCALAPPDATA%\Strata\runtime\app-1.server.log; the engine log is%LOCALAPPDATA%\Strata\install\strata-swift-iq3_xxs.log.Before the restart the same chat was fast because the cache was warm: requests of 308K-315K tokens reused 308K-315K and read only 27-653 tokens in 0.5-3 s. After the restart, the cold reread needs about 324 s at 973.8 tok/s, so the 4-minute watchdog always fires first. Retrying starts the same cold read again, so the chat is effectively stuck after a restart.
The installed 0.2.2 still has
stallMs = 4 * 60_000andturn.lastActivityupdated only from Harness items (host/server/agent/agent-bridge.mjs:118,:427-437). This is the same bug as the report, with restart/cold-cache as the trigger. A fix that counts the engine's/metricsprogress (or the prompt-progress keep-alives) would cover it.For anyone hitting this: keep the app open (warm cache), or start a new chat / branch so the prompt is below about 230K tokens, which fits in 4 minutes at this prefill speed. There is no setting for the watchdog.
Environment: Strata 0.2.2, engine 0.1.47, Windows 11 10.0.26300, RTX 3080 Ti Laptop 16 GB, 64 GB RAM, Swift 1.5 IQ3_XXS, max-context 524288 (the chat is 315,993 tokens).
Agent watchdog: the engine's prompt-read progress counts as activity;…
Niko1221 commentedon Oct 24, 2026
This is fixed in Strata 0.2.3. Please update to the latest release (in the Terminal:
strata update; in the app: download the newest installer from the releases page). If it still happens on the latest version, reply here and we will reopen it. Thank you for the report.wright802 commentedon Oct 27, 2026
working great! Thank you for all you are doing -- Strata is remarkable!
VikenErdogin commentedon Oct 30, 2026
Unfortunately this is still an issue. I just updated to 0.2.8 today (both Stra App on Windows and Strata server which runs on my separate server within my local network) and I still get "The assistant stopped answering for 4 minutes, [...]" errors. At the moment I cannot provide additional details due to personal time constraints but I will try to look into this further, starting with launching the same test run on Hermes and Unsloth in order to determine if it is server, Strata App or both and how my task and workload might affect or trigger it.