Skip to content

bug(runtime): output-free streams closed before response.completed bypass bounded recovery #4599

Description

@jsiu93

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

  1. Start Maka TUI from source:
HOME=<isolated-home> node packages/cli/dist/dev-cli.js
  1. Select gpt-5.6-sol with Full access.
  2. 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.
  3. 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

  1. Preserve a structured "stream closed before protocol completion" signal between model-adapter.ts and ai-sdk-backend.ts, prioritizing AI SDK typed causes.
  2. Route this signal to the existing incomplete-stream recovery counter, reusing incompleteStreamHasNoObservableOutput and step-budget guards.
  3. Preserve original provider diagnostic codes while recording bounded recovery under a dedicated telemetry reason.
  4. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions