FiloSottile/age · error
malformed stanza: %q
Error message
malformed stanza: %q
What it means
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.
Source
Thrown at internal/format/format.go:199
func (r *StanzaReader) ReadStanza() (s *Stanza, err error) {
// Read errors are unrecoverable.
if r.err != nil {
return nil, r.err
}
defer func() { r.err = err }()
s = &Stanza{}
line, err := r.r.ReadBytes('\n')
if err != nil {
return nil, fmt.Errorf("failed to read line: %w", err)
}
if !bytes.HasPrefix(line, stanzaPrefix) {
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 {View on GitHub (pinned to b74dce4cdb)
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
Defensive patterns
Strategy: validation
Validate before calling
// before parsing, sanity-check the producer's output format in tests:
// every stanza line must match: "->" + space-separated printable ASCII args
func stanzaLineOK(line []byte) bool {
if !bytes.HasPrefix(line, []byte("->")) { return false }
fields := strings.Fields(string(line))
return len(fields) >= 2 && fields[0] == "->"
} Try / catch
s, err := r.ReadStanza()
if err != nil && strings.Contains(err.Error(), "malformed stanza:") {
return fmt.Errorf("corrupt stanza header: %w", err)
} Prevention
- 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
When it happens
Trigger: Reading a line like '->\n' (prefix with no type), '- >type' (bad prefix tokenization), or otherwise whitespace-broken stanza headers within an age stream.
Common situations: Corrupted or hand-crafted age files, fuzzing inputs, files transferred in a mode that altered whitespace/newlines (e.g. FTP ASCII mode).
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
- malformed stanza opening line: %q
- invalid ssh-rsa recipient block
- invalid ssh-ed25519 recipient block
- empty line in armored data
- unexpected newline character
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/b533e527815e8937.
Report an issue: GitHub.