nats-io/nats-server · error

ErrIncompleteEntry

ErrIncompleteEntry

Error message

archive: entry not fully written

What it means

ErrIncompleteEntry means a Writer still has an open entry whose declared payload (HeaderSize+PayloadSize) has not been fully written. Writer.WriteHeader throws it if you start a new entry while the previous one still has bytes outstanding, and Writer.Close throws it if the stream is finalized with an unfinished entry. It protects the on-wire format: a reader would otherwise consume the next entry's header as payload.

Source

Thrown at server/archive/archive.go:33

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. Write exactly HeaderSize+PayloadSize bytes for each entry before starting the next one; pad with zeros if the source is short.
  2. Track bytes written per entry and assert they match the declared sizes before calling WriteHeader again or Close.
  3. Restructure so WriteHeader is called only after the full payload is available in memory (buffer it), avoiding mid-entry aborts.
  4. If an entry must be abandoned mid-write, finish the declared size (write filler) since the Writer has no abort API.

Example fix

// before
w.WriteHeader(&archive.Header{Name: "a", PayloadSize: 100})
w.Write(buf[:40])
w.WriteHeader(next) // ErrIncompleteEntry
// after
w.WriteHeader(&archive.Header{Name: "a", PayloadSize: 100})
if _, err := w.Write(buf[:100]); err != nil { return err }
w.WriteHeader(next)
Defensive patterns

Strategy: validation

Validate before calling

// track payload bytes per entry before starting the next
written := int64(0)
declared := hdr.HeaderSize + hdr.PayloadSize
// ... accumulate written via Write returns ...
if written != declared { return fmt.Errorf("entry %q: wrote %d of %d bytes", hdr.Name, written, declared) }

Try / catch

if err := w.WriteHeader(next); err != nil {
    if errors.Is(err, archive.ErrIncompleteEntry) {
        return fmt.Errorf("previous entry under-written by %d bytes", prevDeclared-prevWritten)
    }
    return err
}

Prevention

When it happens

Trigger: Writer.WriteHeader(hdr) called while a.header != nil and a.remaining > 0 (previous entry under-written). Writer.Close() called with an open entry and remaining > 0 — i.e. you wrote fewer payload bytes than declared in the Header.

Common situations: Declaring PayloadSize larger than the bytes actually buffered/produced (e.g. payload computed lazily and error path skipped the remaining writes); early return from an error branch after WriteHeader but before finishing Write; a loop that stops writing on a non-fatal condition; forgetting that a Write call that returned ErrWriteTooLong may have left remaining > 0.

Related errors


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