jdx/mise · error
encrypted tracked files require…
Error message
encrypted tracked files require [history.encryption].recipients; nothing was committed
What it means
capture_tracked_files supports encrypting tracked files, which requires age-style recipient keys from [history.encryption].recipients. If any file's policy requests encryption but no recipients are configured, committing would produce unreadable data, so it bails before committing anything.
Solutions
- Add recipient public keys under [history.encryption].recipients in mise config
- Verify the config file containing recipients is actually loaded (correct scope/profile)
- If encryption wasn't intended, remove or fix the encrypt policy on the matched files
Example fix
# before [history.encryption] # recipients missing # after [history.encryption] recipients = ["age1qy...key1", "age1abc...key2"]
Defensive patterns
Strategy: validation
Validate before calling
let wants_encryption = history.has_encrypted_tracked_files();
let recipients = history.encryption_recipients();
if wants_encryption && recipients.is_empty() {
eprintln!("set [history.encryption].recipients before committing encrypted files");
return Ok(());
} Try / catch
match history.capture_tracked(recipients, interactive) {
Err(e) if e.to_string().contains("recipients; nothing was committed") => {
eprintln!("configure [history.encryption].recipients, then retry");
}
other => other?,
} Prevention
- Configure recipients before enabling any encrypt policy
- Keep [history.encryption] in the global config so it is always loaded
- Test capture after adding encryption rules to catch missing recipients early
When it happens
Trigger: Calling capture_tracked on a walk where at least one file has policy.encrypt = true while recipient_strings is empty — i.e. an encrypt rule matched files but [history.encryption].recipients was never set.
Common situations: Adding an encryption policy/rule to history config without configuring recipients; recipients key present but empty; config in a scoped file that isn't loaded.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- cannot retain an unreadable plaintext version of newly…
- dotfiles: cannot enroll encrypted paths inside an active…
- dotfiles: cannot enroll encrypted paths while history is…
- [history.encryption].recipients must not be empty…
- no age recipients to encrypt for
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/275e09213452b112.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/history/shadow.rs:375
manifest.recipients.sort();
manifest.recipients.dedup();
let mut result = self.capture_tracked_files(walk, &manifest.recipients, interactive)?;
result.tree = manifest.preserve_other_files(self, &result.tree)?;
result.tree = manifest.write(self, &result.tree)?;
Ok(result)
}
fn capture_tracked_files(
&self,
walk: &super::tracked::Walk,
recipient_strings: &[String],
interactive: bool,
) -> Result<CaptureResult> {
if !walk.files.values().any(|(_, policy)| policy.encrypt) {
return self.capture(&walk.roots);
}
if recipient_strings.is_empty() {
bail!(
"encrypted tracked files require [history.encryption].recipients; nothing was committed"
);
}
let mut normalized = recipient_strings.to_vec();
normalized.sort();
normalized.dedup();
let scheme = crate::hash::hash_sha256_to_str(&normalized.join("\n"));
let cache_path = self.dir().parent().unwrap().join("index/encryption.json");
let cache_key = encryption_cache_key(self.dir().parent().unwrap())?;
let mut cache: BTreeMap<String, EncryptionCacheEntry> = std::fs::read(&cache_path)
.ok()
.and_then(|bytes| serde_json::from_slice(&bytes).ok())
.unwrap_or_default();
let mut roots = walk.roots.clone();
let mut overlays = vec![];
let mut recipients = None;
for root in &mut roots {
let encrypted: Vec<_> = rootView on GitHub (pinned to 533346cc37)