nats-io/nats-server · error

failed to read consumer %q state: %w

Error message

failed to read consumer %q state: %w

What it means

Reading the body of a 'consumers/<name>' archive entry with io.ReadAll failed, so the consumer's snapshot state could not be loaded; the restore aborts with the consumer name in the message. As with 951, the raw I/O cause is not wrapped — the underlying reader error surfaced from the s2/tar stream.

Source

Thrown at server/stream_backup.go:422

				if err = fmt.Errorf("failed to activate consumer %q: %w", o.name, err); retErr == nil {
					retErr = err
				}
				s.Warnf("JetStream stream restore for '%s > %s' failed to activate consumers: %v", a.Name, cfg.Name, err)
			}
		}
	}()
	for range nstate.Consumers {
		hdr, err := tr.Next()
		if err != nil {
			return nil, err
		}
		name, found := strings.CutPrefix(hdr.Name, "consumers/")
		if !found {
			return nil, fmt.Errorf("expected consumer, found %q", hdr.Name)
		}
		buf, err := io.ReadAll(tr)
		if err != nil {
			return nil, fmt.Errorf("failed to read consumer %q state: %w", name, err)
		}
		var consumer SnapshotConsumerState
		if err := json.Unmarshal(buf, &consumer); err != nil {
			return nil, fmt.Errorf("failed to decode consumer %q state: %w", name, err)
		}
		if consumer.ConsumerConfig == nil {
			return nil, fmt.Errorf("consumer %q is missing config", name)
		}
		if consumer.ConsumerState == nil {
			return nil, fmt.Errorf("consumer %q is missing state", name)
		}
		isEphemeral := !isDurableConsumer(consumer.ConsumerConfig)
		if isEphemeral {
			// Keep ephemerals alive and interested until all messages have
			// been restored, then start their normal inactivity lifecycle.
			consumer.Durable = name
		}
		o, err := mset.addConsumerForRestore(consumer.ConsumerConfig)

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Re-take or re-transfer the backup and verify checksums before restoring
  2. Remove proxy/upload size or timeout limits blocking large restore bodies, or restore the consumer state via direct file placement on the server
  3. Compare the archive size against the original snapshot; a mismatch confirms truncation — re-download
  4. If only one consumer entry is corrupt and consumers are dispensable, regenerate the snapshot without it (or use `nats stream backup --consumers=false`)
Defensive patterns

Strategy: validation

Validate before calling

if want, got := expectedChecksum, fileChecksum(snapshotPath); want != got {
    return fmt.Errorf("snapshot corrupt/truncated; refusing restore")
}

Try / catch

_, err := acc.RestoreStreamV2(cfg, r)
if err != nil {
    if strings.Contains(err.Error(), "failed to read consumer") {
        // archive unreadable mid-entry: re-transfer snapshot and retry
    }
    return err
}

Prevention

When it happens

Trigger: The compressed bytes for a consumer entry are truncated or corrupt: interrupted backup, partial file transfer, bit-flip corruption in the s2 stream, or a connection drop while streaming the restore request body to STREAM.RESTORE.

Common situations: Large backups transferred over flaky networks; snapshots copied with `cp` while the source backup was still being written; corrupted files on unreliable storage; restore requests aborted mid-stream by proxies with body-size/timeout limits.

Related errors


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