jdx/mise · error
setup configuration itself cannot be encrypted: {path}
Error message
setup configuration itself cannot be encrypted: {path} What it means
decrypt() refuses to decrypt any path that is itself a mise setup control/configuration file. Encryption is only meant for user dotfiles sourced from external locations; encrypting the setup's own configuration would create a circular dependency where mise cannot bootstrap itself. This guard fires before any decryption is attempted.
Source
Thrown at src/system/history/sync/files.rs:255
|| inner.scheme != outer.scheme
|| !layout::is_safe_branch_path(path)
|| !matches!(inner.mode.as_str(), "100644" | "100755" | "120000")
{
bail!("encrypted file does not match its path or mode: {path}");
}
Ok(())
}
pub(crate) fn decrypt(
repo: &HistoryRepo,
path: &str,
object: &Object,
interactive: bool,
) -> Result<Object> {
let outer = envelope(repo, object, agecrypt::MAX_ENCRYPTED_BYTES)?
.ok_or_else(|| eyre::eyre!("missing encrypted file envelope: {path}"))?;
if control_file(path) {
bail!("setup configuration itself cannot be encrypted: {path}");
}
if outer.path != path {
bail!("encrypted file does not match its path: {path}");
}
if let Some(decrypted) = repo.decrypted_object(&object.1) {
return Ok(decrypted);
}
let bytes = agecrypt::decrypt_sync(&outer.ciphertext.0, interactive)
.wrap_err_with(|| format!("cannot unlock {path}; run mise bootstrap dotfiles pull interactively with a matching age identity"))?;
let inner: Plaintext =
rmp_serde::from_slice(&bytes).wrap_err("invalid encrypted file payload")?;
validate(path, &outer, &inner)?;
let oid = repo.transient_blob_id(&inner.content.0)?;
let decrypted = (inner.mode, oid);
repo.remember_decrypted(&object.1, decrypted.clone());
Ok(decrypted)
}
View on GitHub (pinned to afd2eddd3a)
Solutions
- Remove the setup configuration path from the encrypted dotfile sources and keep it as plain tracked config.
- Encrypt only external dotfile sources; store configuration unencrypted in the setup repository.
- Rebuild the history store without the control file (fresh setup store) if it is already committed encrypted.
Example fix
# before [[dotfiles]] source = "~/.config/mise/config.toml" encrypt = true # after [[dotfiles]] source = "~/.config/mise/config.toml" encrypt = false
Defensive patterns
Strategy: validation
Validate before calling
if control_file(path) {
return Err(anyhow!("refusing to encrypt setup configuration: {path}"));
} Try / catch
match decrypt(repo, &object, path, interactive) {
Ok(obj) => obj,
Err(e) if e.to_string().starts_with("setup configuration itself cannot be encrypted") => {
eprintln!("{path} is mise config; track it unencrypted");
read_plain(path)
}
Err(e) => return Err(e),
} Prevention
- Keep mise setup config out of encrypted dotfile source globs.
- Review glob patterns for over-broad matches before enabling encryption.
- Audit which paths are encrypted after changing dotfile configuration.
When it happens
Trigger: Calling decrypt (via commit_object) with a path that control_file(path) classifies as setup configuration (e.g. mise's own config files inside the history store).
Common situations: A user added mise's setup config to the encrypted set in their dotfiles config; a glob/pattern over-broadly matches control files; migrating an old store where config files were wrongly encrypted.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- encrypt an external dotfile source instead of configuration:
- remote cache blob packs require a positive max_pack_bytes li
- no age recipients to encrypt for
- plaintext exceeds the size limit
- compressed payload exceeds the size limit
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/e98efdf1cb4cd9fe.
Report an issue: GitHub.