Skip to content

feat: robust retry/backoff with error classification, partial output protection, and SSE error propagation #12

Description

@danielxxomg

Problem

The current retry logic in src/model.ts has several issues that cause hard failures on transient errors:

1. Thundering herd (no jitter)

The backoff uses pure exponential delay (BASE_DELAY_MS * 2^attempt), meaning all clients hitting the same error retry at identical intervals. This creates synchronized retry storms that amplify server load.

2. No error classification

All errors are treated equally — the code retries on auth failures (unauthorized), quota exceeded (insufficient_credit), and validation errors that will never succeed. This wastes time and delays the inevitable failure.

3. Duplicate content on mid-stream reconnect

streamWithReconnect() can reconnect even after text, reasoning, or tool-calls have already been emitted downstream. Reconnecting regenerates from scratch, producing duplicate content that corrupts the conversation.

4. Silent SSE error handling

In src/stream.ts, SSE error events are enqueued as {type: "error"} stream parts. The AI SDK treats this as regular content and never triggers its own error handling or retry logic. The stream completes "successfully" with an error buried in the content.

5. No HTTP status code retry

HTTP 429 (rate limit) and 5xx (server error) responses are not retried — they throw immediately instead of backing off.

Proposed Solution

Two PRs implement these improvements:

Together they create a clean error propagation chain:

SSE error → controller.error() (#11)
  → streamWithReconnect catches (#10)
    → shouldRetry() checks retryable + emittedContent (#10)
      → reconnect or fail with clear error

Test Coverage

88 tests passing, 0 failures:

  • 22 model tests (9 new for retry logic)
  • 23 stream tests (6 new for error handling)
  • 43 existing tests (unchanged, still passing)

Environment Note

The auth/env tests (index.test.ts, auth.test.ts) are fragile when ~/.commandcode/auth.json exists in the test environment. PR #11 includes a fix that mocks the filesystem in those tests to isolate them from the real environment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions