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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions