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

  1. Check the error from the Header's producing call before calling WriteHeader; never pass a possibly-nil pointer.
  2. Skip or break the loop on io.EOF from Reader.Next instead of forwarding the nil header.
  3. 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

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.