nats-io/nats-server · error

restore for stream '%s > %s' requires reply subject for each

Error message

restore for stream '%s > %s' requires reply subject for each chunk

What it means

JetStream stream restore (file-transfer based restore) protocol error. Every chunk published to the restore delivery subject must carry a reply subject so the server can acknowledge and signal failures/flow control. A chunk without one cancels the restore.

Source

Thrown at server/jetstream_api.go:4258

		if err != nil {
			pr.CloseWithError(err)
		} else {
			pr.Close()
		}
		restoreCh <- struct {
			mset *stream
			err  error
		}{
			mset: mset,
			err:  err,
		}
	})

	processChunk := func(sub *subscription, c *client, _ *Account, subject, reply string, msg []byte) {
		// We require reply subjects to communicate back failures, flow etc. If they do not have one log and cancel.
		if reply == _EMPTY_ {
			sub.client.processUnsub(sub.sid)
			setResult(fmt.Errorf("restore for stream '%s > %s' requires reply subject for each chunk", acc.Name, streamName), reply)
			return
		}
		// Account client messages have \r\n on end. This is an error.
		if len(msg) < LEN_CR_LF {
			sub.client.processUnsub(sub.sid)
			setResult(fmt.Errorf("restore for stream '%s > %s' received short chunk", acc.Name, streamName), reply)
			return
		}
		// Adjust.
		msg = msg[:len(msg)-LEN_CR_LF]

		// This means we are complete with our transfer from the client.
		if len(msg) == 0 {
			s.Debugf("Finished streaming restore for stream '%s > %s'", acc.Name, streamName)
			closeWithError(nil)
			setResult(nil, reply)
			return
		}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Use nats.Request (request-reply) for each restore chunk instead of Publish
  2. Ensure every chunk publication includes a non-empty reply subject
  3. Prefer the client's built-in restore API (jetStream.StreamsRestore / jsm stream restore) over manual chunking

Example fix

// before
js.Publish(restoreSubject, chunk)
// after
js.Restore(&nats.StreamConfig{Name: "S"}) // client handles per-chunk request/reply
// or manually:
resp, err := nc.Request(restoreSubject, chunk, 10*time.Second)
Defensive patterns

Strategy: validation

Validate before calling

if reply == "" {
	return fmt.Errorf("restore chunk publish must use request-reply (non-empty reply subject)")
}

Prevention

When it happens

Trigger: Publishing restore chunks to the $JS.API stream restore subject with Request() replaced by plain Publish, or using a client/subscription that strips the reply subject.

Common situations: Hand-rolled restore implementations over raw NATS; custom tooling that replays recorded restore traffic without reply subjects; intermediate broker/proxy dropping reply fields.

Related errors


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