jdx/mise · error

[history.encryption].recipients must not be empty…

Error message

[history.encryption].recipients must not be empty; configure recipients before capturing encrypted files

What it means

file_recipients reads [history.encryption].recipients from trusted config layers and validates the list is non-empty. An encryption section with no recipients would produce encrypted output readable by nobody, so mise refuses rather than capture files that cannot be decrypted later. The message instructs configuring recipients first.

Solutions

  1. Add at least one recipient to [history.encryption].recipients (e.g. an age public key or GPG key id)
  2. If encryption is not intended yet, remove the [history.encryption] section entirely instead of leaving it empty
  3. Verify the recipients belong to the config layer you intended; the last layer with an encryption section wins

Example fix

# before
[history.encryption]
recipients = []

# after
[history.encryption]
recipients = ["age1qy...@"]
Defensive patterns

Strategy: validation

Validate before calling

recipients=$(tomllib_get history.encryption.recipients 2>/dev/null)  # or parse mise.toml
[ -n "$recipients" ] && [ "$recipients" != "[]" ] || { echo "configure history.encryption.recipients" >&2; exit 1; }

Prevention

When it happens

Trigger: A trusted config file contains a [history.encryption] section whose recipients array is empty ([] or omitted) while any operation resolves encryption recipients.

Common situations: Scaffolding the encryption config by hand and forgetting to add a recipient key; removing all recipients when rotating keys but leaving the encryption section in place.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at src/system/history/config.rs:52

#[serde(deny_unknown_fields)]
pub(crate) struct FileEncryptionConfig {
    #[serde(default)]
    pub recipients: Vec<String>,
}

pub(crate) fn file_recipients() -> Result<Vec<String>> {
    let mut recipients = Vec::new();
    for (path, layer) in layers()? {
        if let Some(encryption) = layer.encryption {
            if !crate::config::config_file::is_trusted(&path) {
                eyre::bail!(
                    "trust the configuration before using its encryption recipients: {}",
                    display_path(&path)
                );
            }
            recipients = encryption.recipients;
            if recipients.is_empty() {
                eyre::bail!(
                    "[history.encryption].recipients must not be empty; configure recipients before capturing encrypted files"
                );
            }
        }
    }
    Ok(recipients)
}

/// `[history.origin]`.
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub(crate) struct OriginTomlConfig {
    pub url: String,
    #[serde(default = "default_branch")]
    pub branch: String,
}

impl OriginTomlConfig {

View on GitHub (pinned to 533346cc37)