FiloSottile/age · error
malformed body line %q: stanza ended without a short line no
Error message
malformed body line %q: stanza ended without a short line note: 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
What it means
A stanza body line failed base64 decoding, and the line itself started with a footer or stanza prefix — meaning a new stanza/footer began without the mandatory short (empty-ish) line that terminates the previous stanza. The message notes files produced by age v1.0.0-beta1–5, which lacked that short line.
Source
Thrown at internal/format/format.go:213
return nil, fmt.Errorf("malformed stanza opening line: %q", line)
}
prefix, args := splitArgs(line)
if prefix != string(stanzaPrefix) || len(args) < 1 {
return nil, fmt.Errorf("malformed stanza: %q", line)
}
s.Type = args[0]
s.Args = args[1:]
for {
line, err := r.r.ReadBytes('\n')
if err != nil {
return nil, fmt.Errorf("failed to read line: %w", err)
}
b, err := DecodeString(strings.TrimSuffix(string(line), "\n"))
if err != nil {
if bytes.HasPrefix(line, footerPrefix) || bytes.HasPrefix(line, stanzaPrefix) {
return 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)
}
return nil, errorf("malformed body line %q: %v", line, err)
}
if len(b) > BytesPerLine {
return nil, errorf("malformed body line %q: too long", line)
}
s.Body = append(s.Body, b...)
if len(b) < BytesPerLine {
// A stanza body always ends with a short line.
return s, nil
}
}
}
type ParseError struct {
err error
}
View on GitHub (pinned to b74dce4cdb)
Solutions
- Decrypt the file with age v1.0.0-beta6 or rage (as the note suggests), re-encrypt with a current version
- Upgrade the encrypting tool so all new files use the stable v1 format
- If the file is current-format, treat this as corruption: re-transfer the file
- Do not hand-edit age headers; regenerate instead
Example fix
// before $ age -d -i key.txt file.age # fails: malformed body line "---": stanza ended without a short line // after $ rage file.age > out.txt # or age v1.0.0-beta6 $ age -e -r <new-pubkey> out.txt > file-v1.age
Defensive patterns
Strategy: validation
Validate before calling
head, _ := bufio.NewReader(f).Peek(64)
// beta age files look similar but stanzas lack the short terminator line
if !bytes.HasPrefix(head, []byte("age-encryption.org/v1")) { return errors.New("not a v1 age file") } Type guard
var pe *age.ParseError
if errors.As(err, &pe) && strings.Contains(pe.Error(), "old beta version") { /* legacy beta format */ } Prevention
- Encrypt with a current stable age release
- Archive files with the encrypting tool's version metadata
- Never hand-edit age headers; regenerate files instead
When it happens
Trigger: format.ReadStanza decodes a body line, DecodeString fails, and bytes.HasPrefix(line, footerPrefix) || bytes.HasPrefix(line, stanzaPrefix) is true — a stanza ended without a short line.
Common situations: Decrypting files generated by old beta versions (age < v1.0.0-beta6 or matching rage betas); hand-edited header files; corruption that made a short line disappear.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- parsing age header:
- failed to parse header: %w
- unexpected newline character
- invalid stanza type: %q
- invalid stanza argument: %q
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/69d748fc53755018.
Report an issue: GitHub.