Bug Description
When the Ollama provider times out or is aborted mid-NDJSON-stream, the model fallback decision log shows reason=unknown instead of reason=timeout. This makes debugging Ollama timeout cascades difficult.
Expected Behavior
Ollama timeout/abort errors should be classified as reason=timeout in model fallback decision logs, consistent with how other provider timeouts are classified.
Actual Behavior
model fallback decision: decision=candidate_failed requested=ollama/minimax-m2.7:cloud candidate=ollama/minimax-m2.7:cloud reason=unknown next=ollama/qwen3.5:cloud
The fallback chain still functions correctly, but reason=unknown is logged instead of reason=timeout.
Root Cause
In auth-profiles-*.js, resolveFailoverReasonFromError() does not handle Ollama-specific abort/NDJSON stream errors. When an Ollama call times out:
- The fetch
AbortController fires and aborts the NDJSON stream reader
- The resulting error has no HTTP status (stream aborted before response completed), no error code, and a message like
"stream closed" or the abort error message
classifyFailoverReasonFromHttpStatus(status, message) → no status number to classify
classifyFailoverReasonFromSymbolicCode(getErrorCode$1(err)) → no code
- Symbolic code list (
ETIMEDOUT, ECONNRESET, etc.) → no match
classifyFailoverReason(message) → no pattern matches "stream closed" or AbortError messages
Result: null → defaults to "unknown" in logModelFallbackDecision().
Impact
- Severity: Low — fallback works correctly, but log visibility for Ollama timeout events is degraded
- Frequency: Every Ollama timeout produces
reason=unknown until fixed
- Debugging cost: Makes it harder to distinguish Ollama timeouts from other failure types in logs
Reproduction
- Configure an Ollama provider with a cloud model that proxies through
https://ollama.com:443
- Trigger a slow response or cold-start timeout (~138s observed in one case)
- Observe model fallback decision log with
reason=unknown
Log excerpt:
[agent/embedded] Profile ollama:default timed out. Trying next account...
[agent/embedded] embedded run failover decision: runId=... stage=assistant decision=fallback_model reason=timeout provider=ollama/minimax-m2.7:cloud profile=sha256:...
[diagnostic] lane task error: lane=main durationMs=138273 error="FailoverError: LLM request timed out."
[model-fallback/decision] model fallback decision: decision=candidate_failed requested=ollama/minimax-m2.7:cloud candidate=ollama/minimax-m2.7:cloud reason=unknown next=ollama/qwen3.5:cloud
Note: reason=timeout is correctly passed to the embedded run failover decision, but the subsequent model fallback decision log shows reason=unknown. This suggests the error is classified correctly at the embedded run layer, but re-classified as unknown during the fallback attempt.
Suggested Fix
In resolveFailoverReasonFromError() in auth-profiles-*.js, add handling for AbortError / stream abort errors from Ollama:
- Add
isAbortError(err) check early in resolveFailoverReasonFromError() to return "timeout" for AbortErrors before attempting HTTP status / symbolic code classification
- Alternatively, extend the message-based
classifyFailoverReason() to match AbortError messages or "stream closed" patterns
This pattern is already used elsewhere in the codebase (e.g., isTimeoutErrorMessage handles "timed out" strings).
Version
OpenClaw 2026.3.28 (f9b1079)
Environment
- OS: Linux (Ubuntu/Debian)
- Install method: npm global (
openclaw)
- Provider: ollama (cloud model via
https://ollama.com:443 proxy)
Bug Description
When the Ollama provider times out or is aborted mid-NDJSON-stream, the model fallback decision log shows
reason=unknowninstead ofreason=timeout. This makes debugging Ollama timeout cascades difficult.Expected Behavior
Ollama timeout/abort errors should be classified as
reason=timeoutin model fallback decision logs, consistent with how other provider timeouts are classified.Actual Behavior
The fallback chain still functions correctly, but
reason=unknownis logged instead ofreason=timeout.Root Cause
In
auth-profiles-*.js,resolveFailoverReasonFromError()does not handle Ollama-specific abort/NDJSON stream errors. When an Ollama call times out:AbortControllerfires and aborts the NDJSON stream reader"stream closed"or the abort error messageclassifyFailoverReasonFromHttpStatus(status, message)→ no status number to classifyclassifyFailoverReasonFromSymbolicCode(getErrorCode$1(err))→ no codeETIMEDOUT,ECONNRESET, etc.) → no matchclassifyFailoverReason(message)→ no pattern matches "stream closed" or AbortError messagesResult:
null→ defaults to"unknown"inlogModelFallbackDecision().Impact
reason=unknownuntil fixedReproduction
https://ollama.com:443reason=unknownLog excerpt:
Note:
reason=timeoutis correctly passed to the embedded run failover decision, but the subsequent model fallback decision log showsreason=unknown. This suggests the error is classified correctly at the embedded run layer, but re-classified asunknownduring the fallback attempt.Suggested Fix
In
resolveFailoverReasonFromError()inauth-profiles-*.js, add handling for AbortError / stream abort errors from Ollama:isAbortError(err)check early inresolveFailoverReasonFromError()to return"timeout"for AbortErrors before attempting HTTP status / symbolic code classificationclassifyFailoverReason()to match AbortError messages or "stream closed" patternsThis pattern is already used elsewhere in the codebase (e.g.,
isTimeoutErrorMessagehandles "timed out" strings).Version
OpenClaw 2026.3.28 (f9b1079)
Environment
openclaw)https://ollama.com:443proxy)