{"record":{"id":"6b6e04c57d7085cb","repo":"FiloSottile/age","slug":"unexpected-newline-character-6b6e04","errorCode":null,"errorMessage":"unexpected newline character","messagePattern":"unexpected newline character","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/format/format.go","lineNumber":36,"sourceCode":"type Header struct {\n\tRecipients []*Stanza\n\tMAC        []byte\n}\n\n// Stanza is assignable to age.Stanza, and if this package is made public,\n// age.Stanza can be made a type alias of this type.\ntype Stanza struct {\n\tType string\n\tArgs []string\n\tBody []byte\n}\n\nvar b64 = base64.RawStdEncoding.Strict()\n\nfunc DecodeString(s string) ([]byte, error) {\n\t// CR and LF are ignored by DecodeString, but we don't want any malleability.\n\tif strings.ContainsAny(s, \"\\n\\r\") {\n\t\treturn nil, errors.New(`unexpected newline character`)\n\t}\n\treturn b64.DecodeString(s)\n}\n\nvar EncodeToString = b64.EncodeToString\n\nconst ColumnsPerLine = 64\n\nconst BytesPerLine = ColumnsPerLine / 4 * 3\n\n// NewWrappedBase64Encoder returns a WrappedBase64Encoder that writes to dst.\nfunc NewWrappedBase64Encoder(enc *base64.Encoding, dst io.Writer) *WrappedBase64Encoder {\n\tw := &WrappedBase64Encoder{dst: dst}\n\tw.enc = base64.NewEncoder(enc, WriterFunc(w.writeWrapped))\n\treturn w\n}\n\ntype WriterFunc func(p []byte) (int, error)","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/format/format.go#L18-L54","documentation":"format.DecodeString decodes the strict base64 used in age headers and stanzas. Go's base64 decoder ignores CR and LF inside input, which would allow header malleability (the same logical key encoded differently), so DecodeString deliberately rejects any string containing \\n or \\r before decoding.","triggerScenarios":"Calling format.DecodeString (directly, or indirectly via age.ParseRecipients, ReadStanza, header parsing, or agessh.unwrap) with a string containing CR/LF — e.g. recipient lines read from a CRLF file, stanza arguments split incorrectly, or user-supplied recipient strings pasted with line breaks.","commonSituations":"Passing recipients from a Windows-edited recipients.txt (CRLF) into age without trimming, building stanzas by hand and accidentally joining fields with newlines, or parsing an age header where a base64 field spans a line boundary due to corruption.","solutions":["Trim newlines/carriage returns from the input string before decoding (strings.TrimSpace / strings.ReplaceAll(s, \"\\r\", \"\")).","Read recipient files with per-line trimming so no CR survives.","Normalize the source data (dos2unix) before feeding it to any age parsing API."],"exampleFix":"// before\nkey, err := format.DecodeString(recipientLine) // recipientLine has trailing \\r\n// after\nkey, err := format.DecodeString(strings.TrimSpace(recipientLine))","handlingStrategy":"validation","validationCode":"// Validate and sanitize before calling format.DecodeString\nfunc safeDecode(s string) ([]byte, error) {\n    if strings.ContainsAny(s, \"\\n\\r\") {\n        return nil, errors.New(\"newline in base64 field\")\n    }\n    return format.DecodeString(strings.TrimSpace(s))\n}","typeGuard":"func isSingleLineB64(s string) bool {\n    return !strings.ContainsAny(s, \"\\n\\r\")\n}","tryCatchPattern":"key, err := format.DecodeString(s)\nif err != nil && strings.Contains(err.Error(), \"unexpected newline character\") {\n    key, err = format.DecodeString(strings.ReplaceAll(s, \"\\r\", \"\"))\n}","preventionTips":["Trim each line with strings.TrimSpace when reading recipient files.","Normalize CRLF files (dos2unix) before parsing age headers/recipients.","Never embed raw newlines when constructing stanza arguments programmatically."],"tags":["go","age","base64","format","newline","crlf"],"backgroundTag":"unexpected-newline","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}