Skip to content

doppler run --watch: SSE keepalive frames fail to parse — "unexpected end of JSON input" every ~30s #536

Description

@abv

Summary

When running doppler run --watch, the CLI logs Doppler Error: unexpected end of JSON input on a ~30s cadence. With --debug enabled you can see it is the analytics event WatchDataParseError firing twice per cycle, with error: "invalid length" and error: "invalid data json". The initial secret fetch and the watched child process are unaffected — it is purely the --watch stream reader.

Version: doppler CLI v3.76.0 (latest)

The server stream is well-formed

Hitting the watch endpoint directly (GET /v3/configs/config/secrets/watch) returns valid SSE:

event: message
data: {"type":"connected"}

event: message
data: {"type":"ping"}        <- every ~30s

So the server side is fine; the failure is client-side in how the stream is read.

Root cause

In performSSERequest (pkg/http), the response body is read with a fixed 1024-byte buffer via repeated response.Body.Read(), and each raw chunk (data[:n]) is passed straight to the watch handler / ParseWatchEvent — with no SSE frame reassembly (no accumulation to the \n\n event boundary, no data: field extraction across reads).

When the server delivers a single SSE event across more than one read — which the periodic keepalive does — each partial fragment is handed to the JSON parser on its own and fails. That matches the two parse errors observed per cycle (invalid length + invalid data json).

Impact

  • Continuous error-log noise (~once per keepalive) for any doppler run --watch.
  • secrets.update events go through the same reader, so live secret-reload is unreliable: whether an update is honored depends on whether its frame happens to land within a single read.

Suggested fix

Buffer reads and split on SSE event boundaries (\n\n), extract the data: payload, and only hand complete frames to the parser.

Reproduction

  1. doppler run --watch -- <any process> against a config (reproduces with a service token).
  2. Watch stderr for ~1 minute; the error appears on each keepalive (~30s).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions