Environment
- Maka commit:
b714a39 (from origin/main at b714a392192961fef3d89d4358fb0a0858252804)
- CLI version:
0.2.0
- OS: macOS
- Model / Provider: Custom relay OpenAI Chat-compatible →
gpt-5.6-sol
- Node.js:
24.19.0
Steps to reproduce
- Start Maka TUI from source:
HOME=<isolated-home> node packages/cli/dist/dev-cli.js
- Select
gpt-5.6-sol with Full access.
- Send a long tool-heavy task (about 45 minutes, ~60 provider steps) through an OpenAI Chat-compatible relay whose upstream closes the stream around 900 s.
- Wait for the upstream connection to close before protocol completion (in our run this hit step
58, attempt 0).
Live triggering depends on upstream behavior; two independent runs (2026-08-30 and 2026-09-03) reproduced this exact pattern at approximately 902 s. The accompanying PR uses a fake stream for deterministic unit reproduction.
Actual
The TUI displays:
Error: stream error: stream disconnected before completion: stream closed before response.completed (code=invalid_request_error)
The persisted error object in runtime_events records:
{
"code": "invalid_request_error",
"kind": "error",
"message": "stream error: stream disconnected before completion: stream closed before response.completed"
}
The database records for this attempt (usage_model_call_attempts) show:
- Step / Attempt:
58 / 0
- Latency:
902245ms
- Time to first token (TTFT):
3026ms
errorClass: Other
providerCode: invalid_request_error
retryable: 0 (false)
status: interrupted
- Number of
provider_retry events for this turn: 0
An isolated classifier probe confirms the diagnostic categorization:
{
"input": {
"type": "invalid_request_error",
"code": "invalid_request_error",
"message": "stream disconnected before completion: stream closed before response.completed"
},
"classifyError": "Other",
"providerRetryMetadata": {
"retryable": false
},
"providerFailureDiagnostic": {
"errorClass": "Other",
"providerCode": "invalid_request_error",
"retryable": false
}
}
Expected
When the physical provider attempt sits at a durable step boundary with no observable text, tool activity, completed steps, or continuation signature, Maka automatically recovers once via bounded recovery.
Any observable output triggers fail-closed termination to preserve durable tool side effects.
If recovery attempts exhaust the budget, Maka presents clear user guidance indicating that sending a message resumes execution from the persisted state.
Why this is Maka's gap
The error code and premature stream termination originate from the upstream relay/provider, while Maka's error classification places this failure into terminal Other, leaving an output-free attempt without bounded recovery.
Proposed invariant
When the current physical provider attempt sits at a durable step boundary with no text, tool activity, completed steps, or continuation signature, a premature stream close automatically recovers at most once; any observable output remains fail-closed, and durable tool side effects never replay.
Specific threshold criteria to enforce:
attemptSawText === false
attemptSawToolActivity === false
attemptSawThinking === false (or following existing idle-recovery contracts for partial thinking)
stepStartedWithProviderContinuation === false
attemptSawCompletedStep === false
- Recovery bounded to the current provider step with a maximum of 1 recovery attempt
- Turn-level cancellation (Stop) continues to take precedence
Proposed fix
- Preserve a structured "stream closed before protocol completion" signal between
model-adapter.ts and ai-sdk-backend.ts, prioritizing AI SDK typed causes.
- Route this signal to the existing incomplete-stream recovery counter, reusing
incompleteStreamHasNoObservableOutput and step-budget guards.
- Preserve original provider diagnostic codes while recording bounded recovery under a dedicated telemetry reason.
- For relays exposing error text only, apply narrow string matching for
stream disconnected before completion and stream closed before response.completed with a single bounded retry.
Related
A draft PR with the narrow fix follows.
Environment
b714a39(fromorigin/mainatb714a392192961fef3d89d4358fb0a0858252804)0.2.0gpt-5.6-sol24.19.0Steps to reproduce
gpt-5.6-solwith Full access.58, attempt0).Live triggering depends on upstream behavior; two independent runs (2026-08-30 and 2026-09-03) reproduced this exact pattern at approximately 902 s. The accompanying PR uses a fake stream for deterministic unit reproduction.
Actual
The TUI displays:
The persisted error object in
runtime_eventsrecords:{ "code": "invalid_request_error", "kind": "error", "message": "stream error: stream disconnected before completion: stream closed before response.completed" }The database records for this attempt (
usage_model_call_attempts) show:58 / 0902245ms3026mserrorClass:OtherproviderCode:invalid_request_errorretryable:0(false)status:interruptedprovider_retryevents for this turn:0An isolated classifier probe confirms the diagnostic categorization:
{ "input": { "type": "invalid_request_error", "code": "invalid_request_error", "message": "stream disconnected before completion: stream closed before response.completed" }, "classifyError": "Other", "providerRetryMetadata": { "retryable": false }, "providerFailureDiagnostic": { "errorClass": "Other", "providerCode": "invalid_request_error", "retryable": false } }Expected
When the physical provider attempt sits at a durable step boundary with no observable text, tool activity, completed steps, or continuation signature, Maka automatically recovers once via bounded recovery.
Any observable output triggers fail-closed termination to preserve durable tool side effects.
If recovery attempts exhaust the budget, Maka presents clear user guidance indicating that sending a message resumes execution from the persisted state.
Why this is Maka's gap
The error code and premature stream termination originate from the upstream relay/provider, while Maka's error classification places this failure into terminal
Other, leaving an output-free attempt without bounded recovery.Proposed invariant
When the current physical provider attempt sits at a durable step boundary with no text, tool activity, completed steps, or continuation signature, a premature stream close automatically recovers at most once; any observable output remains fail-closed, and durable tool side effects never replay.
Specific threshold criteria to enforce:
attemptSawText === falseattemptSawToolActivity === falseattemptSawThinking === false(or following existing idle-recovery contracts for partial thinking)stepStartedWithProviderContinuation === falseattemptSawCompletedStep === falseProposed fix
model-adapter.tsandai-sdk-backend.ts, prioritizing AI SDK typed causes.incompleteStreamHasNoObservableOutputand step-budget guards.stream disconnected before completionandstream closed before response.completedwith a single bounded retry.Related
Otherpath.retryable=falseoccurred after TTFT.A draft PR with the narrow fix follows.