{"record":{"id":"739883fdf6dcb3a6","repo":"FiloSottile/age","slug":"invalid-closing-line-q","errorCode":null,"errorMessage":"invalid closing line: %q","messagePattern":"invalid closing line: %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"armor/armor.go","lineNumber":180,"sourceCode":"\t}\n\t// Reject newline characters ignored by base64.Decode.\n\tif bytes.ContainsAny(line, \"\\n\\r\") {\n\t\treturn 0, r.setErr(errors.New(\"unexpected newline character\"))\n\t}\n\tr.unread = r.buf[:]\n\tn, err := base64.StdEncoding.Strict().Decode(r.unread, line)\n\tif err != nil {\n\t\treturn 0, r.setErr(err)\n\t}\n\tr.unread = r.unread[:n]\n\n\tif n < format.BytesPerLine {\n\t\tline, err := getLine()\n\t\tif err != nil {\n\t\t\treturn 0, r.setErr(err)\n\t\t}\n\t\tif string(line) != Footer {\n\t\t\treturn 0, r.setErr(fmt.Errorf(\"invalid closing line: %q\", line))\n\t\t}\n\t\tr.setErr(drainTrailing())\n\t}\n\n\tnn := copy(p, r.unread)\n\tr.unread = r.unread[nn:]\n\treturn nn, nil\n}\n\ntype Error struct {\n\terr error\n}\n\nfunc (e *Error) Error() string {\n\treturn \"invalid armor: \" + e.err.Error()\n}\n\nfunc (e *Error) Unwrap() error {","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/armor/armor.go#L162-L198","documentation":"The armor reader validates that the final line of the armored stream is exactly the Footer constant (\"-----END AGE ENCRYPTED FILE-----\"). When the remaining short line before EOF does not match, Read returns this error, meaning the armor block is truncated or corrupted at its end.","triggerScenarios":"Calling Read on armor.Reader where the input's closing line differs from the expected footer — the armored file was truncated mid-transfer, extra text was appended or the footer line was edited, or the stream ended after a partial final data line followed by a wrong/missing footer.","commonSituations":"Incomplete file downloads or interrupted scp/rsync transfers cutting off the tail of the file; copy-paste of armor into editors/emails that dropped or altered the last line; tools appending logs after the armor block; diff/merge tooling corrupting the footer line.","solutions":["Re-obtain or re-download the armored file — truncation during transfer is the usual cause; compare file sizes or checksums with the sender","Inspect the end of the file and ensure the last line is exactly \"-----END AGE ENCRYPTED FILE-----\" with no appended content","If extra trailing content exists (e.g. appended text), strip everything after the footer line before decrypting","Ask the sender to re-export/re-send the file with `age -a` if the source itself is damaged"],"exampleFix":"// before\ntail, _ := os.ReadFile(\"msg.age\")\n// file ends with \"---END AGE ENCRYPTED FILE---\" (corrupted footer)\n// armor read fails: \"invalid closing line\"\n\n// after\n// restore/verify footer before reading\nfixed := strings.TrimRight(string(tail), \"\\n\") + \"\\n-----END AGE ENCRYPTED FILE-----\\n\"\nr := armor.NewReader(strings.NewReader(fixed))","handlingStrategy":"validation","validationCode":"func hasAgeArmorFooter(data []byte) bool {\n\ts := string(data)\n\tidx := strings.LastIndex(s, \"-----END AGE ENCRYPTED FILE-----\")\n\tif idx < 0 { return false }\n\treturn strings.TrimSpace(s[idx:]) == \"-----END AGE ENCRYPTED FILE-----\"\n}","typeGuard":null,"tryCatchPattern":"_, err := io.ReadAll(armor.NewReader(f))\nif err != nil && strings.Contains(err.Error(), \"invalid closing line\") {\n\t// treat file as truncated: re-fetch from sender, compare size/checksum\n}","preventionTips":["Verify file size or checksum against the sender after any transfer","Never append text or logs after an armored block in shared files","Compare complete files, not fragments, when copying armor between systems","Check for complete transfer (e.g. rsync -c) before attempting decryption"],"tags":["armor","parsing","truncated-input","age"],"backgroundTag":"invalid-armor-footer","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}