jdx/mise · error

[experimental] No age recipients provided. Use --age-recipie

Error message

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

What it means

age encryption in `mise set` needs at least one recipient to encrypt to. collect_age_recipients() gathers recipients from --age-recipient, --age-ssh-recipient, --age-key-file, and defaults loaded from config (load_recipients_from_defaults); if the combined list is empty it bails with the flag names. The whole feature is experimental and the caller first gates it with ensure_experimental("age encryption").

Source

Thrown at src/cli/set.rs:444

        }

        // 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 6f52dcdf99)

Solutions

  1. Pass a recipient explicitly: `mise set --age-encrypt --age-recipient age1xxxxxxxx FOO=bar`
  2. Or use an SSH public key as recipient: `mise set --age-encrypt --age-ssh-recipient ssh-ed25519 AAAA... FOO=bar`
  3. Or point at a recipients file: `mise set --age-encrypt --age-key-file ~/age-recipients.txt FOO=bar`
  4. Configure recipient defaults in your config so flagless `--age-encrypt` works
  5. Make sure the experimental gate is on: `mise settings set experimental true`

Example fix

# before
mise set --age-encrypt API_TOKEN=xxx   # [experimental] No age recipients provided.

# after
mise settings set experimental true
mise set --age-encrypt --age-recipient age1qy... API_TOKEN=xxx
Defensive patterns

Strategy: validation

Validate before calling

# fail fast with a clear message instead of letting mise bail
recs=()
[ -n "${AGE_RECIPIENT:-}" ] && recs+=(--age-recipient "$AGE_RECIPIENT")
[ -n "${AGE_SSH_RECIPIENT:-}" ] && recs+=(--age-ssh-recipient "$AGE_SSH_RECIPIENT")
[ ${#recs[@]} -gt 0 ] || { echo 'no age recipient configured (set AGE_RECIPIENT or pass --age-recipient)' >&2; exit 2; }
mise set --age-encrypt "${recs[@]}" API_TOKEN="$token"

Type guard

has_recipient() { [ -n "${AGE_RECIPIENT:-}" ] || [ -n "${AGE_SSH_RECIPIENT:-}" ] || [ -f "${AGE_KEY_FILE:-/nonexistent}" ]; }

Try / catch

Catch the recipients bail as a configuration error: print setup instructions (generate an age key via `age-keygen`, or reuse an SSH public key) and abort — retrying without adding a recipient always fails.

Prevention

When it happens

Trigger: Running `mise set --age-encrypt FOO=bar` with none of --age-recipient/--age-ssh-recipient/--age-key-file provided and no recipients configured in config defaults.

Common situations: First use of the experimental age feature without reading its setup; expecting config-file defaults to exist when they were never added; enabling --age-encrypt in shared scripts where the operator forgot to pass a key.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/f2c800eed817f720. Report an issue: GitHub.