jdx/mise · error

encrypt an external dotfile source instead of configuration

Error message

encrypt an external dotfile source instead of configuration: {path}

What it means

Thrown by `encode` when asked to encrypt a file that is itself one of the sync system's control/configuration files (`control_file(path)` is true). The sync configuration must stay plaintext so the tool can read it; encrypting it would make the setup store unreadable. Users should encrypt external dotfiles instead.

Solutions

  1. Remove the setup configuration file from the encryption list.
  2. Refine the glob so only external dotfiles match.
  3. Keep sync configuration in a dedicated directory excluded from encryption.

Example fix

// before
encrypt = ["~/.config/*"]
// after
encrypt = ["~/.config/*", "!~/.config/setup-config.toml"]
Defensive patterns

Strategy: validation

Validate before calling

if (isControlFile(path)) {
  throw new Error(`cannot encrypt setup configuration: ${path}`);
}
await sync.encrypt(path, mode, content);

Type guard

function isExternalDotfile(path: string): boolean {
  return !isControlFile(path);
}

Try / catch

try {
  await sync.encrypt(path, mode, content);
} catch (e) {
  if (/instead of configuration/.test(String(e))) {
    log.warn(`excluded control file from encryption: ${path}`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling `encode` (via `encrypt`) on a path for which `control_file(path)` returns true — i.e. the setup configuration file itself is listed for encryption.

Common situations: Glob patterns like `~/.config/**` that include the sync config file; explicitly adding the setup store's configuration to the encrypted-files list.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/9c645d31e52026cb. Report an issue: GitHub.

Appendix: source

Thrown at src/system/history/sync/files.rs:342

        .map(|recipient| {
            agecrypt::parse_recipient_mode(recipient, interactive)?
                .ok_or_else(|| eyre::eyre!("invalid age recipient: {recipient}"))
        })
        .collect::<Result<Vec<_>>>()?;
    encrypt(repo, path, object, &scheme, &recipients)
}

/// Encrypt bytes before they enter Git. Callers may store only the returned
/// envelope, never the input or the decrypted payload in repository objects.
pub(crate) fn encode(
    path: &str,
    mode: &str,
    content: &[u8],
    scheme: &str,
    recipients: &[Box<dyn age::Recipient + Send>],
) -> Result<Vec<u8>> {
    if control_file(path) {
        bail!("encrypt an external dotfile source instead of configuration: {path}");
    }
    if !matches!(mode, "100644" | "100755" | "120000") {
        bail!("unsupported encrypted file mode: {path}");
    }
    let inner = Plaintext {
        path: path.into(),
        mode: mode.into(),
        scheme: scheme.into(),
        content: Bytes(content.to_vec()),
    };
    let bytes = rmp_serde::to_vec_named(&inner)?;
    let outer = Envelope {
        path: path.into(),
        mode: mode.into(),
        scheme: scheme.into(),
        ciphertext: Bytes(agecrypt::encrypt_bytes(&bytes, recipients)?),
    };
    let mut encoded = MAGIC.to_vec();

View on GitHub (pinned to 533346cc37)