jdx/mise · error

encrypted payload exceeds the size limit

Error message

encrypted payload exceeds the size limit

What it means

After age-encrypting the compressed data, encrypt_bytes verifies the final ciphertext vector does not exceed MAX_ENCRYPTED_BYTES; age framing adds overhead, so even an acceptable compressed input can yield slightly larger output. If out.len() exceeds the limit the result is discarded and the error is raised.

Source

Thrown at src/agecrypt.rs:171

    if recipients.is_empty() {
        bail!("no age recipients to encrypt for");
    }
    if plaintext.len() as u64 > MAX_PLAINTEXT_BYTES {
        bail!("plaintext exceeds the size limit");
    }
    let compressed = zstd::encode_all(plaintext, ZSTD_COMPRESSION_LEVEL)?;
    if compressed.len() as u64 > MAX_ENCRYPTED_BYTES {
        bail!("compressed payload exceeds the size limit");
    }
    let encryptor =
        Encryptor::with_recipients(recipients.iter().map(|r| r.as_ref() as &dyn Recipient))
            .map_err(|e| eyre!("creating the age encryptor: {e}"))?;
    let mut out = Vec::new();
    let mut writer = encryptor.wrap_output(&mut out)?;
    writer.write_all(&compressed)?;
    writer.finish()?;
    if out.len() as u64 > MAX_ENCRYPTED_BYTES {
        bail!("encrypted payload exceeds the size limit");
    }
    Ok(out)
}

pub(crate) async fn decrypt_bytes_mode(
    ciphertext: &[u8],
    interactive: bool,
) -> Result<Vec<u8>, DecryptError> {
    if ciphertext.len() as u64 > MAX_ENCRYPTED_BYTES {
        return Err(DecryptError::Corrupt(
            "encrypted payload exceeds the size limit".into(),
        ));
    }
    let loaded = load_identities(interactive).await;
    if loaded.identities.is_empty() {
        if loaded.plugins > 0 {
            return Err(DecryptError::Failed { error: "hardware identity requires an interactive restore with its age plugin installed".into(), hint: String::new() });
        }

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Reduce input size by a margin that accounts for age overhead (headers, chunk framing).
  2. Check out.len() logic: keep input well below MAX_ENCRYPTED_BYTES rather than exactly at it.
  3. Raise MAX_ENCRYPTED_BYTES in src/agecrypt.rs if the limit no longer fits deployment constraints.

Example fix

// before
let ct = encrypt_bytes(&data, &recipients)?; // fails at the final size check due to age overhead
// after
let budget = MAX_ENCRYPTED_BYTES - 64 * 1024; // leave headroom for age framing
assert!((data.len() as u64) <= budget, "trim data below the encrypted-size budget");
let ct = encrypt_bytes(&data, &recipients)?;
Defensive patterns

Strategy: validation

Validate before calling

let headroom: u64 = 64 * 1024; // age framing overhead
if data.len() as u64 > MAX_ENCRYPTED_BYTES - headroom {
    // trim or split before encrypting
}
encrypt_bytes(&data, &recipients)?;

Type guard

null

Try / catch

match encrypt_bytes(&data, &recipients) {
    Ok(ct) => use(ct),
    Err(e) if e.to_string().contains("encrypted payload exceeds") => retry_with_smaller_input(),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: encrypt_bytes produced ciphertext (compressed payload + age envelope overhead) larger than MAX_ENCRYPTED_BYTES.

Common situations: Input near the compressed-size boundary where age header/chunk overhead pushes the total over the limit; incompressible data just under the raw limit.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/74d150f78cd0e38e. Report an issue: GitHub.