jdx/mise · error

[experimental] No age recipients provided. Use…

Error message

[experimental] No age recipients provided. Use --age-recipient, --age-ssh-recipient, or --age-key-file

What it means

`mise set --encrypt` (experimental age-based encryption) collects age recipients from --age-recipient, --age-ssh-recipient, and --age-key-file. If none of these flags supplied any recipient, encryption cannot produce a valid age header, so mise refuses to proceed. This is an experimental-gated validation of required encryption inputs.

Solutions

  1. Pass at least one recipient, e.g. `mise set --encrypt --age-recipient age1... KEY=value`.
  2. Use `--age-ssh-recipient ssh-ed25519 AAAA...` to encrypt to an SSH key.
  3. Use `--age-key-file <path>` pointing at an age identity/keys file with valid recipients; verify the file loads.

Example fix

// before
mise set --encrypt DB_PASS=secret
// error: no age recipients provided

// after
mise set --encrypt --age-recipient age1qy... DB_PASS=secret
Defensive patterns

Strategy: validation

Validate before calling

test -n "$AGE_RECIPIENT$AGE_SSH_RECIPIENT$AGE_KEY_FILE" || { echo 'provide an age recipient flag' >&2; exit 2; }

Try / catch

mise set --encrypt --age-recipient "$RECIPIENT" KEY=value || { echo 'encryption failed: check recipient/key file' >&2; }

Prevention

When it happens

Trigger: Running `mise set --encrypt KEY=value` (with the experimental age setting enabled) without any of --age-recipient, --age-ssh-recipient, or --age-key-file, or with flags that yield an empty recipient list.

Common situations: Forgetting the recipient flags on first use of encrypted env; providing only a key-file path that fails to load (so recipients stays empty); scripting the encrypt command without forwarding recipient flags.

Understand the failure class

Background: "--flag is required" and "must specify" CLI errors: how missing-required-flag validation works and how to fix it — this error's family across 20 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/f2c800eed817f720. Report an issue: GitHub.

Appendix: source

Thrown at src/cli/set.rs:452

        }

        // If no recipients were provided, use defaults
        if recipients.is_empty()
            && (self.age_recipient.is_empty()
                && self.age_ssh_recipient.is_empty()
                && self.age_key_file.is_none())
        {
            recipients = agecrypt::load_recipients_from_defaults().await?;
        }

        // Load recipients from key file if specified
        if let Some(key_file) = &self.age_key_file {
            let key_file_recipients = agecrypt::load_recipients_from_key_file(key_file).await?;
            recipients.extend(key_file_recipients);
        }

        if recipients.is_empty() {
            bail!(
                "[experimental] No age recipients provided. Use --age-recipient, --age-ssh-recipient, or --age-key-file"
            );
        }

        Ok(recipients)
    }
}

async fn get_mise_toml(filename: &Path) -> Result<MiseToml> {
    let path = env::current_dir()?.join(filename);
    // Before the exists/does-not-exist split, so a `.tool-versions` says why it is refused instead
    // of failing later as invalid TOML, and so a name mise cannot read back is never created.
    crate::config::config_file::ensure_writable_as_toml(&path).await?;
    let mise_toml = if path.exists() {
        MiseToml::from_file(&path)?
    } else {
        MiseToml::init(&path)
    };

View on GitHub (pinned to 533346cc37)