nats-io/nats-server · error · JSStreamOfflineReasonError
JS_STREAM_OFFLINE
JS_STREAM_OFFLINE
Error message
stream is offline
What it means
This JetStream API error (code JS_STREAM_OFFLINE) is returned on a stream info request when the stream exists but has an offlineReason set, meaning it is temporarily unavailable cluster-wide (e.g. created by a migration/restored asset not yet activated). The server responds via NewJSStreamOfflineReasonError wrapping the stream's specific offline reason.
Source
Thrown at server/jetstream_api.go:1640
resp.Error = NewJSStreamMismatchError()
s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
return
}
// Handle clustered version here.
if s.JetStreamIsClustered() {
s.jsClusteredStreamUpdateRequest(ci, acc, subject, reply, copyBytes(rmsg), &cfg, ncfg.Pedantic)
return
}
mset, err := acc.lookupStream(streamName)
if err != nil {
resp.Error = NewJSStreamNotFoundError(Unless(err))
s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
return
}
if mset.offlineReason != _EMPTY_ {
resp.Error = NewJSStreamOfflineReasonError(errors.New(mset.offlineReason))
s.sendDelayedAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp), nil, errRespDelay)
return
}
// Update asset version metadata.
setStaticStreamMetadata(&cfg)
if err := mset.updatePedantic(&cfg, ncfg.Pedantic); err != nil {
resp.Error = NewJSStreamUpdateError(err, Unless(err))
s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
return
}
msetCfg := mset.config()
resp.StreamInfo = &StreamInfo{
Created: mset.createdTime(),
State: mset.state(),
Config: *setDynamicStreamMetadata(&msetCfg),View on GitHub (pinned to 3a66a489d2)
Solutions
- Read the underlying offline reason in the API error payload for the specific cause.
- Wait for the stream's peers to come online / for restore activation to complete, then retry.
- If restored, activate the stream (complete the restore procedure) before issuing requests.
- If peers are permanently down, restore quorum or recreate the stream from backup.
Example fix
// before
si, err := js.StreamInfo("ORDERS") // JS_STREAM_OFFLINE
// after
si, err := js.StreamInfo("ORDERS")
if err != nil {
var apiErr *nats.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode == nats.JSStreamOffline {
// poll/retry until the stream becomes online
return waitForStreamOnline(js, "ORDERS", timeout)
}
return err
} Defensive patterns
Strategy: retry
Validate before calling
func streamOnline(js nats.JetStreamContext, name string) error {
_, err := js.StreamInfo(name)
var apiErr *nats.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode == nats.JSStreamOffline {
return fmt.Errorf("stream %s offline: %s", name, apiErr.Description)
}
return err
} Type guard
func isStreamOffline(err error) bool {
var apiErr *nats.APIError
return errors.As(err, &apiErr) && apiErr.ErrorCode == nats.JSStreamOffline
} Try / catch
err := retry(3, backoff, func() error {
_, err := js.StreamInfo("ORDERS")
if isStreamOffline(err) { return err /* retryable */ }
return nats.ErrEndOfData /* stop */
}) Prevention
- Complete stream restore/activation before serving traffic
- Monitor peer liveness of stream groups in clustered mode
- Poll StreamInfo until offlineReason clears after failover/restore
- Alert on JS_STREAM_OFFLINE responses in client tooling
When it happens
Trigger: Sending a stream info request ($JS.API.STREAM.INFO.<stream>) for a stream whose mset.offlineReason is non-empty; typically streams restored from backup/snapshot pending activation, or streams whose peers are all offline.
Common situations: Querying a stream restored via the restore API before it is activated; running info against a stream whose hosting peers are down; HA failover windows where the stream group has no online peers.
Related errors
- error creating store for stream
- error creating store for consumer
- JS_ERR_GENERIC
- JS_CONSUMER_OFFLINE
- jetstream account not registered
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/be579b5999103535.
Report an issue: GitHub.