Pass a NexusStreamObserver via NexusConfig.Builder#observer(...) to receive structured callbacks for every SSE lifecycle transition. The observer is the canonical way to wire SDK state into ops dashboards, custom telemetry, or load-test tooling - far more reliable than scraping NexusLogger output by substring match.
The observer is opt-in. When NexusConfig.observer() is null the SDK uses NexusStreamObserver.NOOP at zero overhead. The auto-configuration also picks up any NexusStreamObserver bean automatically.
The interface
Every method has an empty default - implement only the hooks you care about.
Hook semantics
FALLBACK_REASON_RATE_LIMITED semantics (v0.3.1+)
Starting in v0.3.1 the "rate-limited" reason fires in two distinct situations:
- Terminal fallback - the 429 response had no parseable
Retry-After header. The SDK terminates the stream loop after firing onFallback("rate-limited") and TTL polling takes over.
- Per-episode notification before reconnect - the 429 response carried a parseable
Retry-After header. The SDK fires onFallback("rate-limited"), sleeps the clamped delay ([5 s, 5 min], interrupt-aware) and then automatically reconnects. The SSE thread does NOT exit, and the rate-limit 429 does NOT count toward MAX_STREAM_ERRORS.
Treat the reason as a rate-limit signal, not as a permanent state change. Use client.isStreamConnected() (or onConnected / onDisconnected) to track the actual connection state.
FALLBACK_REASON_MAX_ERRORS retains its prior meaning: a terminal switch to TTL polling after 3 consecutive transport errors, and the SSE thread exits.
Threading contract
Observer methods are called synchronously from the SDK’s stream-reader Thread. They MUST return promptly. The SDK does not protect against blocking observers.
If your observer does anything substantial (network I/O, lock contention, disk writes), copy the argument and dispatch the work to your own executor before returning.
Spring Boot bean wiring
The auto-configuration picks up any NexusStreamObserver bean automatically:
No additional NexusConfig wiring is needed - the auto-config calls builder.observer(observer) for you.
Counter pattern