nats-io/nats-server · error

ErrWriteTooLong

ErrWriteTooLong

Error message

archive: write exceeds declared entry size

What it means

ErrWriteTooLong means a single Writer.Write call delivered more bytes than the current entry's declared payload size (HeaderSize+PayloadSize). The Writer writes only the bytes that fit, discards the excess logically, and returns n (bytes accepted) plus this error; the archive format has no way to enlarge a declared size mid-entry.

Source

Thrown at server/archive/archive.go:35

	"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. Declare the correct size: buffer the payload first and set PayloadSize (plus HeaderSize) to its actual length before WriteHeader.
  2. Split oversized data across multiple entries or chunk it so each entry's declared size covers all its bytes.
  3. Handle the (int, error) return: on ErrWriteTooLong the excess was dropped — don't retry with the same buffer; re-plan the entry.
  4. Pre-size the entry by writing the payload to a temp buffer/io.Pipe first to learn its length.

Example fix

// before
w.WriteHeader(&archive.Header{Name: "x", PayloadSize: int64(len(hdrBytes))}) // forgot body
w.Write(append(hdrBytes, body...)) // ErrWriteTooLong
// after
payload := append(hdrBytes, body...)
w.WriteHeader(&archive.Header{Name: "x", HeaderSize: int64(len(hdrBytes)), PayloadSize: int64(len(body))})
w.Write(payload)
Defensive patterns

Strategy: validation

Validate before calling

// ensure declared size matches data before WriteHeader
declared := int64(len(data))
if declared != hdr.HeaderSize+hdr.PayloadSize {
    hdr.HeaderSize, hdr.PayloadSize = 0, declared
}

Try / catch

n, err := w.Write(p)
if errors.Is(err, archive.ErrWriteTooLong) {
    return fmt.Errorf("dropped %d excess bytes: declared %d, got %d", len(p)-n, declared, len(p))
}

Prevention

When it happens

Trigger: Writer.Write(p) where len(p) > a.remaining: the entry was declared with a size smaller than the data being pushed, e.g. WriteHeader said PayloadSize=10 but Write sent 100 bytes. Repeated oversized Writes keep failing until the entry is complete.

Common situations: Computing the header size before the payload is fully known (streaming a body larger than estimated); forgetting HeaderSize contributes to the payload total; an io.Copy from a source bigger than declared; passing the Writer to code that writes unbounded amounts (e.g. logging libraries).

Related errors


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