Skip to content

Silently-dropped stream connection recorded as a clean stop #64

Description

@FernandoCelmer

Bug

`GenericProvider._stream` defaults `stop_reason = "stop"` (`pycodeloop/providers/generic.py:386`), only overwritten if a chunk carries `finish_reason` (lines 452-453). The `for raw_line in response:` loop (line 390) just ends normally if the underlying connection drops mid-generation without the socket raising an explicit error (e.g. server closes abruptly, no `[DONE]`, no `finish_reason` chunk).

Impact

Not message loss in the strict sense, but silent corruption: a response truncated by a dropped connection gets persisted to session history labeled `stop_reason="stop"`, indistinguishable from the model finishing on its own. Future turns build on a "complete" assistant message that's actually cut off mid-thought, which can confuse the model or produce incoherent follow-ups without any visible error.

Fix

Track whether the stream loop exited via a `finish_reason`/`[DONE]` marker vs. the iterator simply running out, and surface the difference (e.g. a distinct `stop_reason` like `"connection_lost"`, or raise so the caller's retry logic — the same path already used for `TimeoutError`/`ConnectionError` — can kick in) instead of defaulting to `"stop"`.

Activity

  1. added a commit that references this issue on Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingin progressIssue being actively worked on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions