nats-io/nats-server · error
ErrNilHeader
ErrNilHeader
Error message
archive: nil header
What it means
ErrNilHeader means Writer.WriteHeader was called with a nil *Header pointer. The Writer cannot encode an entry without its metadata (name, sizes, timestamp, sequence), so a nil argument is rejected immediately before anything is written to the underlying stream.
Source
Thrown at server/archive/archive.go:36
"encoding/binary"
"errors"
"io"
)
const MagicBytes = "NATSARC1"
// maxNameLen bounds the entry name length accepted when reading an archive,
// avoiding an unbounded allocation on corrupt or malicious input. The name is
// the only variable-length field read directly from the stream.
const maxNameLen = 1 << 20
var (
ErrClosed = errors.New("archive: closed")
ErrInvalidArchive = errors.New("archive: invalid archive stream")
ErrIncompleteEntry = errors.New("archive: entry not fully written")
ErrNoActiveEntry = errors.New("archive: no active entry")
ErrWriteTooLong = errors.New("archive: write exceeds declared entry size")
ErrNilHeader = errors.New("archive: nil header")
ErrNegativeEntrySize = errors.New("archive: negative entry size")
)
// Header describes one archive entry.
//
// On the wire each entry is the encoded header fields followed by the payload.
// HeaderSize and PayloadSize describe how that payload is split (e.g. message
// headers vs. body); the payload length is their sum and is not stored
// separately. Sequence is always encoded, with 0 meaning "unset".
type Header struct {
Name string
HeaderSize int64
PayloadSize int64
Timestamp int64
Sequence uint64
}
func (h *Header) clone() *Header {View on GitHub (pinned to 3a66a489d2)
Solutions
- Check the error from the Header's producing call before calling WriteHeader; never pass a possibly-nil pointer.
- Skip or break the loop on io.EOF from Reader.Next instead of forwarding the nil header.
- Construct and validate the Header explicitly (name, non-negative sizes) before calling WriteHeader.
Example fix
// before
h, _ := r.Next()
w.WriteHeader(h) // io.EOF -> ErrNilHeader
// after
h, err := r.Next()
if err != nil { return err }
if h == nil { return nil }
if err := w.WriteHeader(h); err != nil { return err } Defensive patterns
Strategy: validation
Validate before calling
if hdr == nil {
return errors.New("cannot start archive entry: header is nil")
}
if hdr.HeaderSize < 0 || hdr.PayloadSize < 0 {
return errors.New("negative entry size")
} Try / catch
if err := w.WriteHeader(hdr); err != nil {
if errors.Is(err, archive.ErrNilHeader) {
return fmt.Errorf("WriteHeader got nil header; check the error from the Header source")
}
return err
} Prevention
- Always check the error from Reader.Next (or any Header producer) before using the returned header — it is nil on io.EOF and errors.
- Break on io.EOF in reader-to-writer copy loops instead of forwarding the nil header.
- Validate headers (non-nil, non-negative sizes) in a helper before WriteHeader.
When it happens
Trigger: Writer.WriteHeader(nil) — typically when the Header was produced by a function that can return nil on an error path (e.g. Reader.Next returning nil on EOF) and the error was not checked before passing it along.
Common situations: Copy-piping entries from a Reader to a Writer: `hdr, err := r.Next(); w.WriteHeader(hdr)` without checking err (Next returns nil header on io.EOF or ErrInvalidArchive); an uninitialized struct pointer that was never assigned; a lookup/map miss returning a nil *Header.
Related errors
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/d40987cd8acbba75.
Report an issue: GitHub.