FiloSottile/age · error

failed to compute stream overhead

Error message

failed to compute stream overhead: %w

What it means

Inspect computes the streaming overhead (nonce + chunk frames) from the encrypted payload size via streamOverhead, which calls stream.PlaintextSize. If the payload is smaller than the 16-byte stream nonce or the size is inconsistent with STREAM framing, this error is returned wrapped.

Solutions

  1. Verify the file is complete and its declared size is correct
  2. Re-transfer the file; a payload smaller than 16 bytes cannot be a valid age payload
  3. Double-check the fileSize argument passed to Inspect (use the real file size; -1 to auto-detect)
  4. Confirm the file was produced by age and not another tool

Example fix

// before
inspect.Inspect(f, size) // size miscounted from armor
// after
st, _ := f.Stat()
inspect.Inspect(f, st.Size()) // let Inspect detect armor and size itself
Defensive patterns

Strategy: validation

Validate before calling

st, _ := f.Stat()
hdrOverhead := int64(200) // generous minimum header size
if st.Size() < hdrOverhead+16 { return fmt.Errorf("file too small to contain a valid age payload") }

Type guard

n/a — stream.PlaintextSize returns an error rather than a type mismatch; check it with errors.Is

Try / catch

data, err := inspect.Inspect(f, size)
if err != nil {
    if strings.Contains(err.Error(), "stream overhead") { return fmt.Errorf("payload size inconsistent with age STREAM framing") }
    return err
}

Prevention

When it happens

Trigger: inspect.Inspect calls streamOverhead(fileSize - data.Sizes.Header) and either payloadSize < 16 or stream.PlaintextSize rejects the encrypted size (e.g., size not congruent with chunk + 16-byte tag framing).

Common situations: Inspecting truncated or corrupted age files; files that are shorter than a single chunk plus nonce; non-age data that got past the header by coincidence; arithmetic on wrong fileSize values passed by the caller.

Related errors


AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31). Data as JSON: /api/errors/87dd440f2fba5cf6. Report an issue: GitHub.

Appendix: source

Thrown at internal/inspect/inspect.go:94

	// (which can have LF or CRLF line endings, varying its size), read to
	// the end to determine it.
	if fileSize == -1 || data.Armor {
		n, err := io.Copy(io.Discard, rest)
		if err != nil {
			return nil, fmt.Errorf("failed to read rest of file: %w", err)
		}
		fileSize = data.Sizes.Header + n
		if !tr.done {
			panic("trackReader not done after io.Copy")
		}
		if tr.count != fileSize && !data.Armor {
			panic("trackReader count mismatch")
		}
		data.Sizes.Armor = tr.count - fileSize
	}
	data.Sizes.Overhead, err = streamOverhead(fileSize - data.Sizes.Header)
	if err != nil {
		return nil, fmt.Errorf("failed to compute stream overhead: %w", err)
	}
	data.Sizes.MinPayload = fileSize - data.Sizes.Header - data.Sizes.Overhead
	data.Sizes.MaxPayload = data.Sizes.MinPayload
	return data, nil
}

type trackReader struct {
	r     io.Reader
	count int64
	done  bool
}

func (tr *trackReader) Read(p []byte) (int, error) {
	if tr.done {
		return 0, io.EOF
	}
	n, err := tr.r.Read(p)
	tr.count += int64(n)

View on GitHub (pinned to b74dce4cdb)