FiloSottile/age · error

invalid stanza type: %q

Error message

invalid stanza type: %q

What it means

format.Stanza.Marshal validates that the stanza Type is a 'valid string' (single line, printable ASCII, no spaces/newlines per isValidString) before writing. A Type containing newlines, spaces, or non-ASCII/control bytes would corrupt the age v1 wire format, so Marshal refuses it.

Source

Thrown at internal/format/format.go:113

	return total, nil
}

// LastLineIsEmpty returns whether the last output line was empty, either
// because no input was written, or because a multiple of BytesPerLine was.
//
// Calling LastLineIsEmpty before Close is meaningless.
func (w *WrappedBase64Encoder) LastLineIsEmpty() bool {
	return w.written%ColumnsPerLine == 0
}

const intro = "age-encryption.org/v1\n"

var stanzaPrefix = []byte("->")
var footerPrefix = []byte("---")

func (r *Stanza) Marshal(w io.Writer) error {
	if !isValidString(r.Type) {
		return fmt.Errorf("invalid stanza type: %q", r.Type)
	}
	for _, a := range r.Args {
		if !isValidString(a) {
			return fmt.Errorf("invalid stanza argument: %q", a)
		}
	}
	if _, err := w.Write(stanzaPrefix); err != nil {
		return err
	}
	if _, err := io.WriteString(w, " "+r.Type); err != nil {
		return err
	}
	for _, a := range r.Args {
		if _, err := io.WriteString(w, " "+a); err != nil {
			return err
		}
	}
	if _, err := io.WriteString(w, "\n"); err != nil {

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Sanitize the Type: trim whitespace/newlines and ensure printable-ASCII, no spaces
  2. Use strings.TrimSpace and reject values with invalid bytes before building the Stanza
  3. Log the offending %q value; it names the exact invalid Type

Example fix

// before
st := &format.Stanza{Type: userProvidedType}
st.Marshal(w)
// after
type1 := strings.TrimSpace(userProvidedType)
if !isValidType(type1) { return fmt.Errorf("bad stanza type %q", type1) }
st := &format.Stanza{Type: type1}
st.Marshal(w)
Defensive patterns

Strategy: validation

Validate before calling

func validStanzaString(s string) bool {
    if s == "" { return false }
    for _, c := range s {
        if c <= 32 || c > 126 { return false }
    }
    return true
}
if !validStanzaString(stanzaType) { return errors.New("stanza type must be printable ASCII, no spaces") }

Type guard

func isPlainASCIIWord(s string) bool {
    return s != "" && strings.IndexFunc(s, func(r rune) bool { return r <= 32 || r > 126 }) < 0
}

Prevention

When it happens

Trigger: Constructing a &format.Stanza{Type: ...} programmatically (e.g. in a plugin or IdentityV1 implementation) with a Type containing a space, '\n', or non-printable/non-ASCII character, then calling Marshal.

Common situations: Custom age plugin stanzas whose type comes from unvalidated user input or config; strings read from files that keep a trailing newline.

Related errors


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