{"record":{"id":"275e09213452b112","repo":"jdx/mise","slug":"encrypted-tracked-files-require-history-encryptio","errorCode":null,"errorMessage":"encrypted tracked files require [history.encryption].recipients; nothing was committed","messagePattern":"encrypted tracked files require \\[history\\.encryption\\]\\.recipients; nothing was committed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/shadow.rs","lineNumber":226,"sourceCode":"        manifest.recipients.sort();\n        manifest.recipients.dedup();\n        let mut result = self.capture_tracked_files(walk, &manifest.recipients, interactive)?;\n        result.tree = manifest.preserve_other_files(self, &result.tree)?;\n        result.tree = manifest.write(self, &result.tree)?;\n        Ok(result)\n    }\n\n    fn capture_tracked_files(\n        &self,\n        walk: &super::tracked::Walk,\n        recipient_strings: &[String],\n        interactive: bool,\n    ) -> Result<CaptureResult> {\n        if !walk.files.values().any(|(_, policy)| policy.encrypt) {\n            return self.capture(&walk.roots);\n        }\n        if recipient_strings.is_empty() {\n            bail!(\n                \"encrypted tracked files require [history.encryption].recipients; nothing was committed\"\n            );\n        }\n        let mut normalized = recipient_strings.to_vec();\n        normalized.sort();\n        normalized.dedup();\n        let scheme = crate::hash::hash_sha256_to_str(&normalized.join(\"\\n\"));\n        let cache_path = self.dir().parent().unwrap().join(\"index/encryption.json\");\n        let cache_key = encryption_cache_key(self.dir().parent().unwrap())?;\n        let mut cache: BTreeMap<String, EncryptionCacheEntry> = std::fs::read(&cache_path)\n            .ok()\n            .and_then(|bytes| serde_json::from_slice(&bytes).ok())\n            .unwrap_or_default();\n        let mut roots = walk.roots.clone();\n        let mut overlays = vec![];\n        let mut recipients = None;\n        for root in &mut roots {\n            let encrypted: Vec<_> = root","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/shadow.rs#L208-L244","documentation":"The shadow-history capture path encrypts tracked files for configured recipients. If any walked file has an `encrypt` policy but no recipient strings were supplied via `[history.encryption].recipients`, capture refuses to proceed and commits nothing, avoiding ciphertext no one can decrypt. It fails fast before any files are hashed or committed.","triggerScenarios":"Calling capture_tracked (via capture_tracked_files) when the walk reports at least one file with policy.encrypt == true while recipient_strings is empty.","commonSituations":"A user enables encryption for a tracked file in their policy but never configures [history.encryption].recipients in their config file, or configures encryption globally but recipients were removed/lost during config migration.","solutions":["Add one or more age recipient public keys under [history.encryption].recipients in the config","If encryption is unintended, disable the encrypt flag on the offending tracked-file policies","Verify the recipients section is in the config file actually being loaded (global vs project)"],"exampleFix":"// before (config.toml)\n[history.encryption]\n# recipients not set\n\n// after (config.toml)\n[history.encryption]\nrecipients = [\"age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p\"]","handlingStrategy":"validation","validationCode":"// Rust (caller-side pre-check)\nanyhow::ensure!(\n    !recipients.is_empty(),\n    \"configure [history.encryption].recipients before capturing encrypted files\"\n);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set [history.encryption].recipients in your global config as soon as you enable any encrypt policy","Add a startup config check that fails early when encrypt policies exist without recipients","Keep recipient keys in version-controlled, encrypted config management"],"tags":["config","encryption","history"],"backgroundTag":"missing-required-config-field","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}