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
- Declare the correct size: buffer the payload first and set PayloadSize (plus HeaderSize) to its actual length before WriteHeader.
- Split oversized data across multiple entries or chunk it so each entry's declared size covers all its bytes.
- Handle the (int, error) return: on ErrWriteTooLong the excess was dropped — don't retry with the same buffer; re-plan the entry.
- 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
- Compute the payload length before WriteHeader; never guess or pre-declare sizes for streaming sources.
- Remember the entry's total size is HeaderSize + PayloadSize combined.
- Check both the byte count and error from Write; oversized bytes are silently truncated from the archive.
- Chunk large payloads into multiple entries with their own (correct) sizes.
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.