nats-io/nats-server · error
expected state.json contents
Error message
expected state.json contents
What it means
After confirming the first archive entry is named 'state.json', RestoreStreamV2 reads its full contents with io.ReadAll. If that read fails (underlying s2/tar reader error, e.g. corrupt compressed data or truncated stream), the restore is aborted with this message. Note the error text is a static string; the underlying read error is not wrapped.
Source
Thrown at server/stream_backup.go:264
// RestoreStreamSnapshotV2 will restore a stream from a snapshot.
func (a *Account) RestoreStreamV2(ncfg *StreamConfig, r io.Reader) (retMset *stream, retErr error) {
dec := s2.NewReader(r)
tr := archive.NewReader(dec)
var nstate StreamState
// Load the stream state.
hdr, err := tr.Next()
if err != nil {
return nil, err
}
if hdr.Name != "state.json" {
return nil, fmt.Errorf("expected state.json first")
}
state, err := io.ReadAll(tr)
if err != nil {
return nil, fmt.Errorf("expected state.json contents")
}
if err := json.Unmarshal(state, &nstate); err != nil {
return nil, fmt.Errorf("error in state.json: %w", err)
}
s, jsa, err := a.checkForJetStream()
if err != nil {
return nil, err
}
js := jsa.js
if js == nil {
return nil, NewJSNotEnabledForAccountError()
}
if _, err := a.lookupStream(ncfg.Name); err == nil {
return nil, NewJSStreamNameExistRestoreFailedError()
}
cfg, apiErr := s.checkStreamCfg(ncfg, a, false)View on GitHub (pinned to 3a66a489d2)
Solutions
- Re-take the stream backup and verify it completes (snapshot ends with the end-of-backup sentinel)
- Check the snapshot file size/checksum against the source to detect truncation before restoring
- Restore over a stable connection; for large streams prefer server-side copy or file-based transport
- If the file is intact, inspect server logs for the underlying s2/tar read error (I/O, disk) and fix the storage/transport issue
Defensive patterns
Strategy: validation
Validate before calling
fi, err := os.Stat(snapshotPath)
if err != nil || fi.Size() == 0 {
return fmt.Errorf("snapshot missing or empty: %s", snapshotPath)
}
if want, got := expectedChecksum, fileChecksum(snapshotPath); want != got {
return fmt.Errorf("snapshot truncated: got %d bytes", got)
} Try / catch
mset, err := acc.RestoreStreamV2(cfg, r)
if err != nil {
if strings.Contains(err.Error(), "expected state.json contents") {
// archive read failed mid-entry: treat snapshot as corrupt, re-transfer
}
return err
} Prevention
- Verify snapshot checksums after every transfer
- Ensure backups complete (look for the end-of-backup sentinel) before moving files
- Restore over reliable connections; avoid proxies with small body limits
- Never restore from files still being written
When it happens
Trigger: The s2-compressed archive data for the state.json entry is truncated mid-read — a partially downloaded/copied snapshot, a backup interrupted before completion (missing the end-of-backup sentinel), or disk/network I/O failure while the server reads the request body during STREAM.RESTORE.
Common situations: Snapshot file cut off by a failed upload over the NATS connection; an s2 file corrupted in transit; a backup taken while the source server was shutting down; restoring over an unreliable network where the restore chunk stream breaks early.
Related errors
- flush of last block failed: %w
- failed to read consumer %q state: %w
- failed to read original block from disk: %w
- restore for stream '%s > %s' requires reply subject for each
- restore for stream '%s > %s' received short chunk
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/7354a575ba04f335.
Report an issue: GitHub.