FiloSottile/age · error
failed to read header: %w
Error message
failed to read header: %w
What it means
internal/inspect.Inspect parses the age header via format.Parse and wraps any parse failure as 'failed to read header'. Since Inspect reports metadata about a file, this means the input is not a well-formed age file or is truncated in the header.
Source
Thrown at internal/inspect/inspect.go:52
data := &Metadata{
Version: "age-encryption.org/v1",
Postquantum: "yes",
}
tr := &trackReader{r: r}
br := bufio.NewReader(tr)
const maxWhitespace = 1024
start, _ := br.Peek(maxWhitespace + len(armor.Header))
if strings.HasPrefix(string(bytes.TrimSpace(start)), armor.Header) {
r = armor.NewReader(br)
data.Armor = true
} else {
r = br
}
hdr, rest, err := format.Parse(r)
if err != nil {
return nil, fmt.Errorf("failed to read header: %w", err)
}
buf := &bytes.Buffer{}
if err := hdr.Marshal(buf); err != nil {
return nil, fmt.Errorf("failed to re-serialize header: %w", err)
}
data.Sizes.Header = int64(buf.Len())
for _, s := range hdr.Recipients {
data.StanzaTypes = append(data.StanzaTypes, s.Type)
switch s.Type {
case "X25519", "ssh-rsa", "ssh-ed25519", "p256tag", "piv-p256":
data.Postquantum = "no"
case "mlkem768x25519", "scrypt", "mlkem768p256tag":
// Keep "yes".
default:
if data.Postquantum != "no" {
data.Postquantum = "unknown"View on GitHub (pinned to b74dce4cdb)
Solutions
- Confirm the target file is age-encrypted and complete before inspecting
- If PEM-armored, inspect the decoded stream or ensure armor handling is used
- Print the wrapped cause (errors.As *age.ParseError) to identify the offending line
- Re-download the file if the size looks short
Example fix
// before
data, err := inspect.Inspect(f, size) // failed to read header
// after
head, _ := bufio.NewReader(f).Peek(len("age-encryption.org"))
if !bytes.HasPrefix(head, []byte("age-encryption")) { return fmt.Errorf("not an age file") }
f.Seek(0, 0)
data, err := inspect.Inspect(f, size) Defensive patterns
Strategy: try-catch
Validate before calling
head, _ := bufio.NewReader(f).Peek(len("age-encryption.org/v1"))
if !bytes.HasPrefix(head, []byte("age-encryption.org/v1")) { return errors.New("age-inspect: not an age file") } Type guard
var pe *age.ParseError
if errors.As(err, &pe) { /* header-level failure from format.Parse */ } Try / catch
data, err := inspect.Inspect(f, size)
if err != nil {
var pe *age.ParseError
if errors.As(err, &pe) { return fmt.Errorf("cannot inspect: %w", pe.Unwrap()) }
return err
} Prevention
- Check that the input starts with the age intro line before inspecting
- Handle armored input separately (armor.NewReader)
- Validate file completeness before metadata analysis
When it happens
Trigger: inspect.Inspect (called by the age-inspect command's main, or tests) calls format.Parse(r) and err != nil — non-age input, corrupted or truncated header.
Common situations: Running age-inspect on plaintext files, wrong extensions, partially uploaded files, or armored files passed without armor handling.
Related errors
- malformed body line %q: stanza ended without a short line no
- parsing age header:
- failed to read header: %w
- failed to parse header: %w
- failed to re-serialize header: %w
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/3a53e0794ad79ce9.
Report an issue: GitHub.