nats-io/nats-server · error

restore for stream ' > ' is stalled

Error message

restore for stream '%s > %s' is stalled

What it means

JetStream stream restore inactivity timeout. During a restore the server arms an activity timer; if no new chunk arrives within the interval, the restore is considered stalled and is cancelled with this error.

Solutions

  1. Ensure the client keeps sending chunks continuously or restarts the restore on failure
  2. Increase throughput / reduce per-chunk processing time on the client; read the file in a tight loop
  3. Check network stability between client and server; retry the restore after a failure
Defensive patterns

Strategy: retry

Validate before calling

// ensure a fast, uninterrupted chunk producer before starting
if !canReadFileContinuously(restoreFile) {
	return fmt.Errorf("restore source must supply chunks without long pauses")
}

Try / catch

err := restoreStream(cfg, r)
if err != nil && strings.Contains(err.Error(), "is stalled") {
	// restart restore; resume/checkpoint if supported
	return restoreStream(cfg, reopenReader())
}

Prevention

When it happens

Trigger: Client stops sending chunks (crash, slow producer, network partition) without closing the restore session, leaving the notActive timer to fire after activityInterval.

Common situations: Client application killed mid-restore; WAN/slow link where chunk generation takes longer than the inactivity window; back-pressure from client-side file reading pauses exceeding the interval.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/175468ff554c8e4b. Report an issue: GitHub.

Appendix: source

Thrown at server/jetstream_api.go:4434

				restoreDone = true
				restoreResult = rr
				if inputDone {
					err := inputErr
					if err == nil {
						err = rr.err
					}
					finish(replySubj, err, rr.mset)
					return
				}
			case <-activeQ.ch:
				if n, ok := activeQ.popOne(); ok {
					total += n
					if !inputDone {
						notActive.Reset(activityInterval)
					}
				}
			case <-notActive.C:
				err := fmt.Errorf("restore for stream '%s > %s' is stalled", acc.Name, streamName)
				closeWithError(err)
				doneCh <- err
				return
			}
		}
	})

	return doneCh
}

// Process a snapshot request.
func (s *Server) jsStreamSnapshotRequest(sub *subscription, c *client, _ *Account, subject, reply string, rmsg []byte) {
	if c == nil || !s.JetStreamEnabled() {
		return
	}
	ci, acc, hdr, msg, err := s.getRequestInfo(c, rmsg)
	if err != nil {
		s.Warnf(badAPIRequestT, msg)

View on GitHub (pinned to 3a66a489d2)