FiloSottile/age · error
malformed stanza opening line: %q
Error message
malformed stanza opening line: %q
What it means
A stanza must begin with the literal prefix '->'. ReadStanza checks bytes.HasPrefix after reading the line; if the line doesn't start with '->', the stream is not a stanza boundary and parsing stops with the offending line quoted (%q) in the error.
Source
Thrown at internal/format/format.go:195
func NewStanzaReader(r *bufio.Reader) *StanzaReader {
return &StanzaReader{r: r}
}
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)View on GitHub (pinned to b74dce4cdb)
Solutions
- Confirm the input is an age v1 file produced by age encrypt
- Check the file wasn't edited, re-wrapped, or concatenated after encryption
- Look at the quoted line in the error to identify where format expectations diverge
Defensive patterns
Strategy: try-catch
Validate before calling
// quick pre-check that the input begins like an age file
head, _ := bufio.NewReader(f).Peek(2)
if string(head) == "" { return errors.New("empty input") } Try / catch
s, err := r.ReadStanza()
if err != nil && strings.Contains(err.Error(), "malformed stanza opening line") {
return fmt.Errorf("input is not an age v1 stream: %w", err)
} Prevention
- Only feed age-encrypt output to the parser; sniff format first if input origin is unknown
- Avoid post-encryption edits to encrypted files
- Quote/preserve the offending line from the error when reporting bugs
When it happens
Trigger: Parse/readStanza encountering a file where a stanza header is expected but the line starts with something else (e.g. '---' footer, 'wrong' MAC line, or random text); feeding a plaintext or foreign-format file to the age parser.
Common situations: Decrypting a file that isn't age-format, an encrypted file whose body was modified/reordered, concatenating age files, editors that mangled the header.
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: %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/577863d587681228.
Report an issue: GitHub.