{"record":{"id":"a73290abfac6b65a","repo":"jdx/mise","slug":"history-encryption-recipients-must-not-be-empty","errorCode":null,"errorMessage":"[history.encryption].recipients must not be empty; configure recipients before capturing encrypted files","messagePattern":"\\[history\\.encryption\\]\\.recipients must not be empty; configure recipients before capturing encrypted files","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/config.rs","lineNumber":52,"sourceCode":"#[serde(deny_unknown_fields)]\npub(crate) struct FileEncryptionConfig {\n    #[serde(default)]\n    pub recipients: Vec<String>,\n}\n\npub(crate) fn file_recipients() -> Result<Vec<String>> {\n    let mut recipients = Vec::new();\n    for (path, layer) in layers()? {\n        if let Some(encryption) = layer.encryption {\n            if !crate::config::config_file::is_trusted(&path) {\n                eyre::bail!(\n                    \"trust the configuration before using its encryption recipients: {}\",\n                    display_path(&path)\n                );\n            }\n            recipients = encryption.recipients;\n            if recipients.is_empty() {\n                eyre::bail!(\n                    \"[history.encryption].recipients must not be empty; configure recipients before capturing encrypted files\"\n                );\n            }\n        }\n    }\n    Ok(recipients)\n}\n\n/// `[history.origin]`.\n#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]\n#[serde(deny_unknown_fields)]\npub(crate) struct OriginTomlConfig {\n    pub url: String,\n    #[serde(default = \"default_branch\")]\n    pub branch: String,\n}\n\nimpl OriginTomlConfig {","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/history/config.rs#L34-L70","documentation":"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.","triggerScenarios":"A trusted config file contains a [history.encryption] section whose recipients array is empty ([] or omitted) while any operation resolves encryption recipients.","commonSituations":"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.","solutions":["Add at least one recipient to [history.encryption].recipients (e.g. an age public key or GPG key id)","If encryption is not intended yet, remove the [history.encryption] section entirely instead of leaving it empty","Verify the recipients belong to the config layer you intended; the last layer with an encryption section wins"],"exampleFix":"# before\n[history.encryption]\nrecipients = []\n\n# after\n[history.encryption]\nrecipients = [\"age1qy...@\"]","handlingStrategy":"validation","validationCode":"recipients=$(tomllib_get history.encryption.recipients 2>/dev/null)  # or parse mise.toml\n[ -n \"$recipients\" ] && [ \"$recipients\" != \"[]\" ] || { echo \"configure history.encryption.recipients\" >&2; exit 1; }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair [history.encryption] with at least one recipient key","Validate config with the mise JSON schema before committing","Remove the whole encryption section if encryption is not being used"],"tags":["encryption","config","validation"],"backgroundTag":"empty-required-field","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}