{"record":{"id":"e2d2d789cf788270","repo":"aaif-goose/goose","slug":"ollama-stream-stalled-no-data-received-for-s-t","errorCode":null,"errorMessage":"Ollama stream stalled: no data received for {}s. This may indicate the model is overwhelmed by the request payload. Try a smaller model, reduce the number of tools, or increase the timeout via OLLAMA_STREAM_TIMEOUT, GOOSE_STREAM_TIMEOUT, or OLLAMA_TIMEOUT in your config.","messagePattern":"Ollama stream stalled: no data received for (.+?)s\\. This may indicate the model is overwhelmed by the request payload\\. Try a smaller model, reduce the number of tools, or increase the timeout via OLLAMA_STREAM_TIMEOUT, GOOSE_STREAM_TIMEOUT, or OLLAMA_TIMEOUT in your config\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/goose-providers/src/ollama.rs","lineNumber":495,"sourceCode":"    stream: impl futures::Stream<Item = anyhow::Result<String>> + Unpin + Send + 'static,\n    timeout_secs: u64,\n) -> std::pin::Pin<Box<dyn futures::Stream<Item = anyhow::Result<String>> + Send>> {\n    let timeout = Duration::from_secs(timeout_secs);\n    Box::pin(try_stream! {\n        let mut stream = stream;\n\n        // Allow time-to-first-token to be governed by the request timeout.\n        // Only enforce per-chunk timeout after first SSE line arrives.\n        match stream.next().await {\n            Some(first_item) => yield first_item?,\n            None => return,\n        }\n        loop {\n            match tokio::time::timeout(timeout, stream.next()).await {\n                Ok(Some(item)) => yield item?,\n                Ok(None) => break,\n                Err(_) => {\n                    Err::<(), anyhow::Error>(anyhow::anyhow!(\n                        \"Ollama stream stalled: no data received for {}s. \\\n                         This may indicate the model is overwhelmed by the request payload. \\\n                         Try a smaller model, reduce the number of tools, or increase the \\\n                         timeout via OLLAMA_STREAM_TIMEOUT, GOOSE_STREAM_TIMEOUT, or \\\n                         OLLAMA_TIMEOUT in your config.\",\n                        timeout_secs\n                    ))?;\n                }\n            }\n        }\n    })\n}\n\n/// Ollama-specific streaming handler with XML tool call fallback.\n/// Uses the Ollama format module which buffers text when XML tool calls are detected,\n/// preventing duplicate content from being emitted to the UI.\n/// Timeout is applied at the raw SSE line level via with_line_timeout so that\n/// buffering inside response_to_streaming_message_ollama does not cause false stalls.","sourceCodeStart":477,"sourceCodeEnd":513,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-providers/src/ollama.rs#L477-L513","documentation":"goose wraps the Ollama SSE stream with a per-chunk watchdog: after the first SSE line arrives, every subsequent line must arrive within a timeout or the stream aborts with this error. The budget resolves from OLLAMA_STREAM_TIMEOUT, then GOOSE_STREAM_TIMEOUT, then OLLAMA_TIMEOUT, else the 120-second default (OLLAMA_DEFAULT_CHUNK_TIMEOUT_SECS). Time-to-first-token is deliberately exempt; only gaps between chunks count. It fires when the server stops sending mid-generation — typically an overwhelmed local model, OOM/crash of the ollama process, or a network/proxy drop.","triggerScenarios":"Streaming a large prompt (dozens of MCP tools, long context) to a slow backend — CPU-only inference, big parameter counts, deep reasoning models pausing between reasoning and output — where one inter-chunk gap exceeds the timeout; or the ollama daemon being OOM-killed / the connection severed mid-stream.","commonSituations":"Laptops running 70B models on CPU; goose sessions with many enabled MCP extensions so the tool schema bloats every request; reverse proxies (nginx/Cloudflare) with idle read timeouts shorter than the model's think time; ollama loading a second model and swapping VRAM mid-response.","solutions":["Raise the budget: export OLLAMA_STREAM_TIMEOUT=600 (checked first), or GOOSE_STREAM_TIMEOUT / OLLAMA_TIMEOUT; note OLLAMA_TIMEOUT also caps the overall request, so raise it too for long generations","Shrink the payload: disable unneeded MCP tools/extensions, trim conversation history, lower num_ctx/context limit","Use a smaller/quantized model or offload less (check 'ollama ps'); for CPU inference, budget minutes per response, not seconds","Check ollama's own logs (journalctl -u ollama or OLLAMA_DEBUG=1) for OOM kills or model load errors — if the process died, no timeout increase will help","If a proxy sits in the path, raise its read/idle timeout above OLLAMA_STREAM_TIMEOUT"],"exampleFix":"# before\nexport OLLAMA_TIMEOUT=600   # per-chunk gap still capped at 120s default -> stalls abort\n\n# after\nexport OLLAMA_STREAM_TIMEOUT=600   # per-chunk watchdog\nexport OLLAMA_TIMEOUT=900           # overall request budget\n# plus: goose session --no-extensions (or disable unused tools) to shrink the payload","handlingStrategy":"retry","validationCode":"fn chunk_timeout_secs() -> u64 {\n    [\"OLLAMA_STREAM_TIMEOUT\", \"GOOSE_STREAM_TIMEOUT\", \"OLLAMA_TIMEOUT\"]\n        .iter().find_map(|k| std::env::var(k).ok().and_then(|v| v.parse().ok()))\n        .unwrap_or(120)\n}\n// Before long jobs: ensure the budget fits the backend, e.g. bump for CPU inference:\n// if running_on_cpu() { std::env::set_var(\"OLLAMA_STREAM_TIMEOUT\", \"600\"); }","typeGuard":null,"tryCatchPattern":"// Distinguish a stall (retryable, payload-driven) from a dead server (not retryable):\nlet mut attempt = 0;\nloop {\n    attempt += 1;\n    match run_stream(&req).await {\n        Err(e) if e.to_string().contains(\"stream stalled\") && attempt < 3 => {\n            tracing::warn!(\"stall on attempt {attempt}; trimming tools and retrying\");\n            req = trim_tools(req, 0.5).await; // shrink payload between attempts\n            continue;\n        }\n        other => break other,\n    }\n}?;","preventionTips":["Set OLLAMA_STREAM_TIMEOUT to match the slowest realistic inter-chunk gap (minutes for CPU/big models), and raise OLLAMA_TIMEOUT to cover total generation","Keep enabled MCP tools minimal — tool schemas ride along on every request and slow first tokens/chunks","Watch ollama health (ollama ps, dmesg for OOM) so you distinguish an overloaded model from a crashed daemon","Raise proxy idle/read timeouts above the stream timeout when a reverse proxy fronts Ollama"],"tags":["ollama","timeout","streaming","network","performance"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}