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
- Sanitize the Type: trim whitespace/newlines and ensure printable-ASCII, no spaces
- Use strings.TrimSpace and reject values with invalid bytes before building the Stanza
- 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
- TrimSpace all externally sourced stanza fields
- Base64-encode anything that may contain spaces/newlines/binary
- Write a unit test marshalling every stanza your plugin emits
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
- invalid stanza argument: %q
- invalid ssh-rsa recipient block
- invalid ssh-ed25519 recipient block
- unexpected newline character
- malformed stanza opening line: %q
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/a5ac7db664adb3f0.
Report an issue: GitHub.