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<_> = root

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Add one or more age recipient public keys under [history.encryption].recipients in the config
  2. If encryption is unintended, disable the encrypt flag on the offending tracked-file policies
  3. 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

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


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/275e09213452b112. Report an issue: GitHub.