Skip to content

EOFError in SSE streaming thread is re-raised, prevents reconnection #624

Description

@dan98765

Description

When the SSE streaming connection drops with an EOFError, the SDK's error handling re-raises the exception as a RuntimeError, which propagates out and kills the streaming thread. This prevents the SyncManager from receiving a PUSH_RETRYABLE_ERROR notification, so the SDK never falls back to polling or attempts to reconnect.

In our production environment, we see ~25 EOFError events per day, each generating 4 log lines (including raw puts to stdout). The connections eventually recover on a timer, but the reconnection path through push_status_handler is bypassed.

SDK Version

Tested on 8.10.1, but the same pattern exists on master.

Root Cause

In lib/splitclient-rb/sse/event_source/client.rb, the connect_stream method has nested error handling for EOFError:

# Inner rescue (around line 109)
rescue EOFError => e
  puts "SSE read operation EOF Exception!: #{e.inspect}"
  @config.logger.error("SSE read operation EOF Exception!: #{e.inspect}")
  raise 'eof exception'   # re-raises as RuntimeError

# Outer rescue (around line 130)
rescue RuntimeError
  raise 'eof exception'   # propagates out, kills the thread

The double-raise means the exception escapes the thread entirely. The SyncManager's push_status_handler never receives PUSH_RETRYABLE_ERROR, so the reconnection/polling fallback path is never triggered.

Additionally, the puts on line 110 writes directly to stdout, bypassing the configured logger.

Expected Behavior

An EOFError during SSE streaming should:

  1. Log the event through the configured logger (not puts)
  2. Notify the SyncManager via PUSH_RETRYABLE_ERROR so it can trigger reconnection or fall back to polling
  3. Not kill the streaming thread

I see PR #622 touches some of the reconnection logic, but the specific double-raise pattern and puts call remain.

Suggested Fix

Replace the re-raise with a push_status notification, similar to how other connection errors are handled:

rescue EOFError => e
  @config.logger.error("SSE EOF: #{e.inspect}")
  @on[:action].call(Splits::EventSource::Actions::PUSH_RETRYABLE_ERROR)

Happy to submit a PR for either or both of these if helpful.

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