nats-io/nats-server · error
restore for stream '%s > %s' received short chunk
Error message
restore for stream '%s > %s' received short chunk
What it means
JetStream stream restore chunk validation error. Restore chunks are published as normal NATS messages, which must end with CRLF (\r\n); a message smaller than the CRLF length (or otherwise missing the terminator) is treated as a short/invalid chunk and the restore is cancelled.
Source
Thrown at server/jetstream_api.go:4264
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
}
// Signal activity before and after the blocking write.
// The pre-write signal refreshes the stall watchdog when the
// chunk arrives; the post-write signal refreshes it again once
// RestoreStream has consumed the data. This keeps the idle
// window between chunks anchored to the end of the previousView on GitHub (pinned to 3a66a489d2)
Solutions
- Send raw bytes with a trailing \r\n as the NATS wire protocol requires
- Ensure chunk boundaries align with the byte ranges the server granted, never sending empty chunks
- Use the official client restore API which handles framing correctly
Example fix
// before nc.Request(subj, chunk[:len(chunk)-2], timeout) // stripped CRLF // after nc.Request(subj, chunk, timeout) // chunk framed as received, CRLF intact
Defensive patterns
Strategy: validation
Validate before calling
if len(chunk) < 2 || chunk[len(chunk)-2] != '\r' || chunk[len(chunk)-1] != '\n' {
return fmt.Errorf("restore chunk missing CRLF terminator")
} Type guard
func chunkWellFormed(chunk []byte) bool {
return len(chunk) >= 2 && chunk[len(chunk)-2] == '\r' && chunk[len(chunk)-1] == '\n'
} Prevention
- Never trim or transform chunk bytes between read and publish
- Verify chunk sizes against the server-granted byte ranges
When it happens
Trigger: Publishing a zero-length or truncated chunk to the restore subject; a chunk whose trailing \r\n was stripped by intermediate processing or custom serialization.
Common situations: Hand-rolled restore tools that split data and trim line endings; sending the final zero-byte 'done' payload incorrectly; corruption when replaying captured traffic through a transformer.
Related errors
- restore for stream '%s > %s' requires reply subject for each
- 10062
- restore for stream '%s > %s' is stalled
- error creating store for stream
- error creating store for consumer
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/5c2ff940eb0af5b6.
Report an issue: GitHub.