FiloSottile/age · error

trailing data after armored file

Error message

trailing data after armored file

What it means

When the armored reader encounters the footer, drainTrailing reads up to maxWhitespace extra bytes to confirm only whitespace follows the armored file. If any non-whitespace bytes are found after the footer, the reader reports this error, since valid ASCII armor must end right after the footer line.

Source

Thrown at armor/armor.go:123

		line, err := r.r.ReadBytes('\n')
		if err == io.EOF && len(line) == 0 {
			return nil, io.ErrUnexpectedEOF
		} else if err != nil && err != io.EOF {
			return nil, err
		}
		line = bytes.TrimSuffix(line, []byte("\n"))
		line = bytes.TrimSuffix(line, []byte("\r"))
		return line, nil
	}

	const maxWhitespace = 1024
	drainTrailing := func() error {
		buf, err := io.ReadAll(io.LimitReader(r.r, maxWhitespace))
		if err != nil {
			return err
		}
		if len(bytes.TrimSpace(buf)) != 0 {
			return errors.New("trailing data after armored file")
		}
		if len(buf) == maxWhitespace {
			return errors.New("too much trailing whitespace")
		}
		return io.EOF
	}

	var removedWhitespace int
	for !r.started {
		line, err := getLine()
		if err != nil {
			return 0, r.setErr(err)
		}
		// Ignore leading whitespace.
		if len(bytes.TrimSpace(line)) == 0 {
			removedWhitespace += len(line) + 1
			if removedWhitespace > maxWhitespace {
				return 0, r.setErr(errors.New("too much leading whitespace"))

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Remove everything after the footer line from the armored file (extract only the BEGIN...END block).
  2. If concatenation was intended, split the file and decrypt each armored section separately.
  3. Re-transfer or re-export the file to get a clean armored document.

Example fix

// before: file.age contains armor followed by extra text
cat a.age b.age > c.age
// after
cat a.age > c.age
# or decrypt a.age and b.age separately
Defensive patterns

Strategy: validation

Validate before calling

// Validate an armored file ends right after the footer
func hasTrailingData(data []byte) bool {
    marker := []byte("--- END AGE ENCRYPTED FILE---")
    i := bytes.LastIndex(data, marker)
    if i < 0 {
        return false
    }
    return len(bytes.TrimSpace(data[i+len(marker):])) > 0
}

Try / catch

_, err := io.ReadAll(armor.NewReader(f))
if err != nil && err.Error() == "trailing data after armored file" {
    // extract only the BEGIN..END block and retry
}

Prevention

When it happens

Trigger: Reading (via armor.NewReader, e.g. through age.Decrypt on an armored file) a stream that contains additional non-whitespace content after the '--- END AGE ENCRYPTED FILE ---' line — concatenated armored files, appended notes/signatures, or binary junk after the armor.

Common situations: Concatenating two armored age files with cat and decrypting the result, scripts appending logs/signatures after the armor block, or downloads that include HTML/error text after the armored payload.

Related errors


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