FiloSottile/age · error

column limit exceeded

Error message

column limit exceeded

What it means

Armor lines are restricted to format.ColumnsPerLine (64) characters, matching the encoder's output. A longer line means the data was not produced by this armor format (or was reformatted), so the reader rejects it instead of guessing how to split it.

Source

Thrown at armor/armor.go:161

			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 {
			return 0, r.setErr(err)
		}
		if string(line) != Footer {

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Re-wrap body lines to at most 64 columns (e.g. fold -w 64) and retry decryption.
  2. Re-obtain the armored file from its original source or re-run the age CLI to produce correctly wrapped armor.
  3. Disable editor word-wrap and avoid MIME re-encoding when transferring .age files.

Example fix

// before: one 200-char base64 line in the armor body
// after
fold -w 64 longline.age > wrapped.age
Defensive patterns

Strategy: validation

Validate before calling

// Check armor line widths before decrypting
func checkLineLengths(data []byte) error {
    for _, l := range bytes.Split(data, []byte('\n')) {
        l = bytes.TrimRight(l, "\r")
        if !bytes.HasPrefix(l, []byte("-----")) && len(l) > 64 {
            return fmt.Errorf("line too long: %d", len(l))
        }
    }
    return nil
}

Type guard

func isWrappedArmorLine(line []byte) bool {
    return len(line) <= 64
}

Try / catch

_, err := io.ReadAll(armor.NewReader(f))
if err != nil && err.Error() == "column limit exceeded" {
    // re-wrap lines with fold -w 64 or re-export the armor
}

Prevention

When it happens

Trigger: Reading an armored file where a body line exceeds 64 characters — e.g. armor reflowed by a text editor with a different wrap width, MIME base64 from email (76-char lines) pasted into an age armor envelope, or hand-concatenated base64 lines.

Common situations: Extracting base64 from a MIME email attachment into an age armor template, editors with word-wrap rewriting the file, or scripts joining base64 lines into one long line.

Related errors


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