jdx/mise · error
encrypted tracked files require [history.encryption].recipie
Error message
encrypted tracked files require [history.encryption].recipients; nothing was committed
What it means
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.
Source
Thrown at src/system/history/shadow.rs:226
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 afd2eddd3a)
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)
Example fix
// before (config.toml) [history.encryption] # recipients not set // after (config.toml) [history.encryption] recipients = ["age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p"]
Defensive patterns
Strategy: validation
Validate before calling
// Rust (caller-side pre-check)
anyhow::ensure!(
!recipients.is_empty(),
"configure [history.encryption].recipients before capturing encrypted files"
); Prevention
- 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
When it happens
Trigger: Calling capture_tracked (via capture_tracked_files) when the walk reports at least one file with policy.encrypt == true while recipient_strings is empty.
Common situations: 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.
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
- no age recipients to encrypt for
- history is disabled (history.enabled = false)
- encrypted dotfile {target} requires an external source, not
- cannot retain an unreadable plaintext version of newly encry
- trust the configuration before using its encryption recipien
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/275e09213452b112.
Report an issue: GitHub.