{"record":{"id":"f6de92e026407f59","repo":"FiloSottile/age","slug":"failed-to-parse-header-w","errorCode":null,"errorMessage":"failed to parse header: %w","messagePattern":"failed to parse header: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/format/format.go","lineNumber":358,"sourceCode":"\t\t\t}\n\n\t\t\tprefix, args := splitArgs(line)\n\t\t\tif prefix != string(footerPrefix) || len(args) != 1 {\n\t\t\t\treturn nil, nil, errorf(\"malformed closing line: %q\", line)\n\t\t\t}\n\t\t\th.MAC, err = DecodeString(args[0])\n\t\t\tif err != nil || len(h.MAC) != 32 {\n\t\t\t\treturn nil, nil, errorf(\"malformed closing line %q: %v\", line, err)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tif len(h.Recipients) == maxRecipientStanzas {\n\t\t\treturn nil, nil, errorf(\"header contains more than %d recipient stanzas\", maxRecipientStanzas)\n\t\t}\n\n\t\ts, err := sr.ReadStanza()\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to parse header: %w\", err)\n\t\t}\n\t\th.Recipients = append(h.Recipients, s)\n\t}\n\tif len(h.Recipients) == 0 {\n\t\treturn nil, nil, errorf(\"no recipient stanzas\")\n\t}\n\n\t// If input is a bufio.Reader, rr might be equal to input because\n\t// bufio.NewReader short-circuits. In this case we can just return it (and\n\t// we would end up reading the buffer twice if we prepended the peek below).\n\tif rr == input {\n\t\treturn h, rr, nil\n\t}\n\t// Otherwise, unwind the bufio overread and return the unbuffered input.\n\tbuf, err := rr.Peek(rr.Buffered())\n\tif err != nil {\n\t\treturn nil, nil, errorf(\"internal error: %v\", err)\n\t}","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/format/format.go#L340-L376","documentation":"format.Parse reads each recipient stanza with ReadStanza; any failure there (I/O error, malformed body line, too-long line) is wrapped as 'failed to parse header'. The inner error carries the precise cause (see ParseError.Unwrap).","triggerScenarios":"format.Parse calls sr.ReadStanza() while collecting recipient stanzas and receives an error — invalid base64 body, oversized line, or EOF mid-stanza (see errors 60–61).","commonSituations":"Corrupted .age files from bad transfers; old beta-format files; concatenated or hand-modified headers; more than 16 stanzas also rejected (distinct message) but often from automation loops appending stanzas.","solutions":["Unwrap with errors.Is/As to find the root cause (EOF vs malformed line)","Re-transfer or re-encrypt the file; a broken stanza cannot be repaired","Check for legacy beta formats and decrypt with rage / age v1.0.0-beta6 if applicable","Limit stanza counts if generating headers programmatically (max 16)"],"exampleFix":"// before\nif err != nil { log.Fatal(err) } // failed to parse header: malformed body line ...\n// after\nvar pe *age.ParseError\nif errors.As(err, &pe) { log.Fatalf(\"bad header at cause: %v\", pe.Unwrap()) }","handlingStrategy":"try-catch","validationCode":"// sanity-check structure before deep parsing\nhead, _ := bufio.NewReader(f).Peek(128)\nif !bytes.Contains(head, []byte(\"-> \")) && !bytes.Contains(head, []byte(\"---\")) { return errors.New(\"header missing recipient stanzas\") }","typeGuard":"var pe *age.ParseError\nif errors.As(err, &pe) { cause := pe.Unwrap() /* io.EOF vs malformed line */ }","tryCatchPattern":"hdr, _, err := format.Parse(r)\nif err != nil {\n    var pe *age.ParseError\n    if errors.As(err, &pe) { return fmt.Errorf(\"bad stanza: %w\", pe.Unwrap()) }\n    return err\n}","preventionTips":["Re-transfer corrupted files instead of patching them","Cap recipient stanza counts (max 16) in generators","Decrypt legacy beta files with rage / age v1.0.0-beta6"],"tags":["format","header-parsing","corruption"],"backgroundTag":"malformed-header","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}