golang/go · error

archive/tar: missed writing %d bytes

Error message

archive/tar: missed writing %d bytes

What it means

Returned by (*tar.Writer).Flush (writer.go:57) when the current file still has unwritten bytes — tw.curr.logicalRemaining() > 0. Flush is documented to require the current file be fully written first; calling it early (before writing Header.Size bytes) violates that contract.

Source

Thrown at src/archive/tar/writer.go:57

type fileWriter interface {
	io.Writer
	fileState

	ReadFrom(io.Reader) (int64, error)
}

// Flush finishes writing the current file's block padding.
// The current file must be fully written before Flush can be called.
//
// This is unnecessary as the next call to [Writer.WriteHeader] or [Writer.Close]
// will implicitly flush out the file's padding.
func (tw *Writer) Flush() error {
	if tw.err != nil {
		return tw.err
	}
	if nb := tw.curr.logicalRemaining(); nb > 0 {
		return fmt.Errorf("archive/tar: missed writing %d bytes", nb)
	}
	if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil {
		return tw.err
	}
	tw.pad = 0
	return nil
}

// WriteHeader writes hdr and prepares to accept the file's contents.
// The Header.Size determines how many bytes can be written for the next file.
// If the current file is not fully written, then this returns an error.
// This implicitly flushes any padding necessary before writing the header.
func (tw *Writer) WriteHeader(hdr *Header) error {
	if err := tw.Flush(); err != nil {
		return err
	}
	tw.hdr = *hdr // Shallow copy of Header

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Ensure you write exactly hdr.Size bytes after WriteHeader before calling Flush/WriteHeader/Close.
  2. If the true size is unknown, stream to a buffer/counter first and set Size to the counted length.
  3. Do not call Flush unless you specifically need block padding mid-stream; rely on WriteHeader/Close implicit flush.
  4. If a write fails partway, abort the whole archive (the tar is already corrupt).

Example fix

// before
hdr := &tar.Header{Name: "f", Size: 1024, Mode: 0644}
tw.WriteHeader(hdr)
io.CopyN(tw, src, 512) // only half
tw.Flush() // -> missed writing 512 bytes

// after
hdr := &tar.Header{Name: "f", Size: 1024, Mode: 0644}
tw.WriteHeader(hdr)
io.Copy(tw, io.LimitReader(src, 1024)) // exactly Size
tw.Flush()
Defensive patterns

Strategy: validation

Validate before calling

// Count content before writing the header so Size is exact.
func writeExact(tw *tar.Writer, name string, r io.Reader) error {
    var buf bytes.Buffer
    n, _ := io.Copy(&buf, r)
    h := &tar.Header{Name: name, Mode: 0644, Size: int64(buf.Len())}
    if err := tw.WriteHeader(h); err != nil {
        return err
    }
    _, err := io.CopyN(tw, &buf, n)
    return err
}

Prevention

When it happens

Trigger: Calling Writer.Flush (or WriteHeader/Close, which call Flush implicitly) after WriteHeader with a declared Size but before writing exactly Size bytes of content. Also if the writer's content writer short-wrote or the caller stopped early.

Common situations: Declared header Size larger than the actual data streamed (e.g., set Size but io.Copy from a shorter reader); caller returns early from a walk without finishing the file; misuse of Flush as a generic sync point; mismatch between ContentLength and bytes written.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/0633437ddef709cd. Report an issue: GitHub.