FiloSottile/age · error

empty line in armored data

Error message

empty line in armored data

What it means

Inside the armored body, each line must be a non-empty base64 line. An empty line between the header and footer is structurally invalid, so the reader reports this error rather than skipping it, keeping the armor format strict and unambiguous.

Source

Thrown at armor/armor.go:158

			if removedWhitespace > maxWhitespace {
				return 0, r.setErr(errors.New("too much leading whitespace"))
			}
			continue
		}
		if string(line) != Header {
			return 0, r.setErr(fmt.Errorf("invalid first line: %q", line))
		}
		r.started = true
	}
	line, err := getLine()
	if err != nil {
		return 0, r.setErr(err)
	}
	if string(line) == Footer {
		return 0, r.setErr(drainTrailing())
	}
	if len(line) == 0 {
		return 0, r.setErr(errors.New("empty line in armored data"))
	}
	if len(line) > format.ColumnsPerLine {
		return 0, r.setErr(errors.New("column limit exceeded"))
	}
	// Reject newline characters ignored by base64.Decode.
	if bytes.ContainsAny(line, "\n\r") {
		return 0, r.setErr(errors.New("unexpected newline character"))
	}
	r.unread = r.buf[:]
	n, err := base64.StdEncoding.Strict().Decode(r.unread, line)
	if err != nil {
		return 0, r.setErr(err)
	}
	r.unread = r.unread[:n]

	if n < format.BytesPerLine {
		line, err := getLine()
		if err != nil {

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Remove the blank line(s) from the armored body so the payload is contiguous 64-column base64 lines.
  2. Regenerate the armored file from the original encrypted payload.
  3. Verify with a text editor that the body has no empty lines between BEGIN and END.

Example fix

// before
-----BEGIN AGE ENCRYPTED FILE-----
YWJj...

ZGVm...
-----END AGE ENCRYPTED FILE-----
// after
-----BEGIN AGE ENCRYPTED FILE-----
YWJj...
ZGVm...
-----END AGE ENCRYPTED FILE-----
Defensive patterns

Strategy: validation

Validate before calling

// Reject blank lines inside the armor body before decrypting
func validateArmorBody(data []byte) error {
    inBody := false
    for _, l := range bytes.Split(data, []byte('\n')) {
        s := strings.TrimSpace(string(l))
        if strings.HasPrefix(s, "-----BEGIN") {
            inBody = true
            continue
        }
        if strings.HasPrefix(s, "-----END") {
            return nil
        }
        if inBody && s == "" {
            return errors.New("empty line in armored data")
        }
    }
    return errors.New("no footer")
}

Try / catch

_, err := io.ReadAll(armor.NewReader(f))
if err != nil && err.Error() == "empty line in armored data" {
    // remove blank lines from the armor body and retry
}

Prevention

When it happens

Trigger: Reading an armored file whose payload contains a blank line — typically from editors inserting blank lines, mail clients reformatting, or manual edits to the armored data.

Common situations: Pasting armor through email clients that mangle line structure, formatting tools that insert blank lines at column boundaries, or diff/merge artifacts leaving an empty line in the base64 body.

Related errors


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