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

  1. Re-obtain the encrypted file in binary-safe mode (no ASCII-mode transfer, no text normalization)
  2. Validate the producer: the file was likely generated by a non-conforming writer
  3. 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

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

Related errors


AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31). Data as JSON: /api/errors/b533e527815e8937. Report an issue: GitHub.