{"record":{"id":"9837a0a88337148f","repo":"nats-io/nats-server","slug":"errincompleteentry","errorCode":"ErrIncompleteEntry","errorMessage":"archive: entry not fully written","messagePattern":"archive: entry not fully written","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/archive/archive.go","lineNumber":33,"sourceCode":"\nimport (\n\t\"bufio\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n)\n\nconst MagicBytes = \"NATSARC1\"\n\n// maxNameLen bounds the entry name length accepted when reading an archive,\n// avoiding an unbounded allocation on corrupt or malicious input. The name is\n// the only variable-length field read directly from the stream.\nconst maxNameLen = 1 << 20\n\nvar (\n\tErrClosed            = errors.New(\"archive: closed\")\n\tErrInvalidArchive    = errors.New(\"archive: invalid archive stream\")\n\tErrIncompleteEntry   = errors.New(\"archive: entry not fully written\")\n\tErrNoActiveEntry     = errors.New(\"archive: no active entry\")\n\tErrWriteTooLong      = errors.New(\"archive: write exceeds declared entry size\")\n\tErrNilHeader         = errors.New(\"archive: nil header\")\n\tErrNegativeEntrySize = errors.New(\"archive: negative entry size\")\n)\n\n// Header describes one archive entry.\n//\n// On the wire each entry is the encoded header fields followed by the payload.\n// HeaderSize and PayloadSize describe how that payload is split (e.g. message\n// headers vs. body); the payload length is their sum and is not stored\n// separately. Sequence is always encoded, with 0 meaning \"unset\".\ntype Header struct {\n\tName        string\n\tHeaderSize  int64\n\tPayloadSize int64\n\tTimestamp   int64\n\tSequence    uint64","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/archive/archive.go#L15-L51","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Write exactly HeaderSize+PayloadSize bytes for each entry before starting the next one; pad with zeros if the source is short.","Track bytes written per entry and assert they match the declared sizes before calling WriteHeader again or Close.","Restructure so WriteHeader is called only after the full payload is available in memory (buffer it), avoiding mid-entry aborts.","If an entry must be abandoned mid-write, finish the declared size (write filler) since the Writer has no abort API."],"exampleFix":"// before\nw.WriteHeader(&archive.Header{Name: \"a\", PayloadSize: 100})\nw.Write(buf[:40])\nw.WriteHeader(next) // ErrIncompleteEntry\n// after\nw.WriteHeader(&archive.Header{Name: \"a\", PayloadSize: 100})\nif _, err := w.Write(buf[:100]); err != nil { return err }\nw.WriteHeader(next)","handlingStrategy":"validation","validationCode":"// track payload bytes per entry before starting the next\nwritten := int64(0)\ndeclared := hdr.HeaderSize + hdr.PayloadSize\n// ... accumulate written via Write returns ...\nif written != declared { return fmt.Errorf(\"entry %q: wrote %d of %d bytes\", hdr.Name, written, declared) }","typeGuard":null,"tryCatchPattern":"if err := w.WriteHeader(next); err != nil {\n    if errors.Is(err, archive.ErrIncompleteEntry) {\n        return fmt.Errorf(\"previous entry under-written by %d bytes\", prevDeclared-prevWritten)\n    }\n    return err\n}","preventionTips":["Always write exactly HeaderSize+PayloadSize bytes; pad short payloads rather than abandoning entries.","Accumulate the int returned by Write and compare against the declared total after each entry.","Call Close and check its error — ErrIncompleteEntry at Close is the last chance to detect an unfinished entry.","Buffer the payload first so the declared size in WriteHeader is exact."],"tags":["go","archive","writer","protocol-violation"],"backgroundTag":"incomplete-write","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}