{"record":{"id":"b533e527815e8937","repo":"FiloSottile/age","slug":"malformed-stanza-q","errorCode":null,"errorMessage":"malformed stanza: %q","messagePattern":"malformed stanza: %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/format/format.go","lineNumber":199,"sourceCode":"func (r *StanzaReader) ReadStanza() (s *Stanza, err error) {\n\t// Read errors are unrecoverable.\n\tif r.err != nil {\n\t\treturn nil, r.err\n\t}\n\tdefer func() { r.err = err }()\n\n\ts = &Stanza{}\n\n\tline, err := r.r.ReadBytes('\\n')\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read line: %w\", err)\n\t}\n\tif !bytes.HasPrefix(line, stanzaPrefix) {\n\t\treturn nil, fmt.Errorf(\"malformed stanza opening line: %q\", line)\n\t}\n\tprefix, args := splitArgs(line)\n\tif prefix != string(stanzaPrefix) || len(args) < 1 {\n\t\treturn nil, fmt.Errorf(\"malformed stanza: %q\", line)\n\t}\n\ts.Type = args[0]\n\ts.Args = args[1:]\n\n\tfor {\n\t\tline, err := r.r.ReadBytes('\\n')\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to read line: %w\", err)\n\t\t}\n\n\t\tb, err := DecodeString(strings.TrimSuffix(string(line), \"\\n\"))\n\t\tif err != nil {\n\t\t\tif bytes.HasPrefix(line, footerPrefix) || bytes.HasPrefix(line, stanzaPrefix) {\n\t\t\t\treturn nil, fmt.Errorf(\"malformed body line %q: stanza ended without a short line\\nnote: this might be a file encrypted with an old beta version of age or rage; use age v1.0.0-beta6 or rage to decrypt it\", line)\n\t\t\t}\n\t\t\treturn nil, errorf(\"malformed body line %q: %v\", line, err)\n\t\t}\n\t\tif len(b) > BytesPerLine {","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/format/format.go#L181-L217","documentation":"After the '->' prefix, the opening line must split into the prefix plus at least one argument (the stanza type), and the prefix must be exactly '->' with no glued tokens. If splitArgs yields a different prefix or zero args, the stanza is structurally malformed and cannot be routed to an identity.","triggerScenarios":"Reading a line like '->\\n' (prefix with no type), '- >type' (bad prefix tokenization), or otherwise whitespace-broken stanza headers within an age stream.","commonSituations":"Corrupted or hand-crafted age files, fuzzing inputs, files transferred in a mode that altered whitespace/newlines (e.g. FTP ASCII mode).","solutions":["Re-obtain the encrypted file in binary-safe mode (no ASCII-mode transfer, no text normalization)","Validate the producer: the file was likely generated by a non-conforming writer","Inspect the quoted line from the error to pinpoint the malformation"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before parsing, sanity-check the producer's output format in tests:\n// every stanza line must match: \"->\" + space-separated printable ASCII args\nfunc stanzaLineOK(line []byte) bool {\n    if !bytes.HasPrefix(line, []byte(\"->\")) { return false }\n    fields := strings.Fields(string(line))\n    return len(fields) >= 2 && fields[0] == \"->\"\n}","typeGuard":null,"tryCatchPattern":"s, err := r.ReadStanza()\nif err != nil && strings.Contains(err.Error(), \"malformed stanza:\") {\n    return fmt.Errorf(\"corrupt stanza header: %w\", err)\n}","preventionTips":["Transfer encrypted files in binary mode; ASCII-mode transfers corrupt line structure","Test producers (including plugins) against the reference parser","Reject hand-crafted streams; generate files only with a spec-conformant writer"],"tags":["age","format","parsing","malformed-input"],"backgroundTag":"malformed-stanza","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}