nats-io/nats-server · error

ErrNoActiveEntry

ErrNoActiveEntry

Error message

archive: no active entry

What it means

ErrNoActiveEntry means Writer.Write was called without an open archive entry. Every payload byte must belong to an entry started by WriteHeader; after an entry's declared size is fully written, the Writer clears its internal header, so further Writes are rejected. It is an API-misuse error on the write side of the archive format.

Source

Thrown at server/archive/archive.go:34

import (
	"bufio"
	"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
}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Call WriteHeader with a valid Header before every run of Write calls.
  2. Match Write calls 1:1 with entries: stop writing once you've written HeaderSize+PayloadSize bytes for the current entry.
  3. If bytes must precede the first entry, wrap a separate io.Writer for them; the archive stream itself must start with entries (the Writer emits the magic on the first WriteHeader).
  4. Track a 'current entry open' flag in your code mirroring a.header != nil.

Example fix

// before
w := archive.NewWriter(f)
w.Write(payload) // ErrNoActiveEntry
// after
w := archive.NewWriter(f)
if err := w.WriteHeader(&archive.Header{Name: "entry", HeaderSize: 0, PayloadSize: int64(len(payload))}); err != nil { return err }
if _, err := w.Write(payload); err != nil { return err }
Defensive patterns

Strategy: validation

Validate before calling

// guard your own write helper
func writeEntry(w *archive.Writer, h *archive.Header, payload []byte) error {
    if h == nil { return errors.New("nil header") }
    if err := w.WriteHeader(h); err != nil { return err }
    _, err := w.Write(payload)
    return err
}

Try / catch

if _, err := w.Write(p); err != nil {
    if errors.Is(err, archive.ErrNoActiveEntry) {
        return fmt.Errorf("Write called outside an entry; call WriteHeader first")
    }
    return err
}

Prevention

When it happens

Trigger: Writer.Write(p) called before any WriteHeader; Write called after the current entry's remaining bytes reached 0 (header auto-cleared, e.g. a zero-size entry or after writing the exact declared payload); Write called on a brand-new Writer with only a stale header pointer.

Common situations: Wrapping the Writer in an io.MultiWriter or passing it where an io.Writer is expected and writing preamble/metadata bytes outside of any entry; a caller that keeps writing after the payload loop ended (off-by-one or trailing newline flush); reusing a Writer variable whose last entry already completed.

Related errors


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