jdx/mise · error · eyre::Report

[dotfiles]."{}": source does not exist: {}

Error message

[dotfiles]."{}": source does not exist: {}

What it means

When building an OCI image, `[oci.dotfiles]` entries with mode symlink, copy, or symlink-each copy content from a local source path into the image. Before collecting files, mise verifies that the source path exists; a missing path aborts the build, naming the entry's key and the offending path. Mode 'content' (inline text) is exempt because it has no source file.

Source

Thrown at src/oci/builder.rs:866

fn resolve_layer_owner(opts_owner: Option<LayerOwner>, oci: &OciConfig) -> LayerOwner {
    opts_owner.unwrap_or_else(|| {
        let uid = oci.user_id.unwrap_or(0);
        let gid = oci.group_id.unwrap_or(uid);
        LayerOwner::new(uid, gid)
    })
}

fn build_dotfiles_layer(
    cfg: &Config,
    requests: &[FileRequest],
    owner: LayerOwner,
) -> Result<LayerBlob> {
    let mut entries = DotfilesLayerEntries::default();

    for req in requests {
        if req.mode != FileMode::Content && !req.source.exists() {
            bail!(
                "[dotfiles].\"{}\": source does not exist: {}",
                req.target_raw,
                req.source.display()
            );
        }

        match req.mode {
            FileMode::Symlink | FileMode::Copy => {
                collect_source_as_files(&req.source, &oci_target_path(req)?, &mut entries)
                    .wrap_err_with(|| {
                        format!("adding [dotfiles].\"{}\" to OCI image", req.target_raw)
                    })?;
            }
            FileMode::SymlinkEach => {
                if !req.source.is_dir() {
                    bail!(
                        "[dotfiles].\"{}\": mode symlink-each requires a directory source: {}",
                        req.target_raw,

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Create the source file, or point the entry at a path that exists on the build machine
  2. For machine-independent content, switch to `mode = "content"` with inline text instead of a source path
  3. Use absolute paths (and correct tilde handling) for dotfile sources in CI contexts

Example fix

# before (mise.toml)
[oci.dotfiles]
"~/.gitconfig" = { source = "./gitconfig", mode = "copy" }
# but ./gitconfig does not exist

# after (mise.toml)
[oci.dotfiles]
"/root/.gitconfig" = { mode = "content", content = "[user]\n\tname = CI\n" }
Defensive patterns

Strategy: validation

Validate before calling

python3 - <<'EOF'
import tomllib, os, sys
cfg = tomllib.load(open('mise.toml', 'rb'))
for target, spec in (cfg.get('oci', {}).get('dotfiles', {}) or {}).items():
    mode = spec.get('mode', 'symlink') if isinstance(spec, dict) else 'symlink'
    src = spec.get('source') if isinstance(spec, dict) else None
    if mode != 'content' and src and not os.path.exists(os.path.expanduser(src)):
        sys.exit(f'dotfiles {target!r}: missing source {src}')
print('dotfile sources OK')
EOF

Prevention

When it happens

Trigger: A mise.toml `[oci.dotfiles]` entry like `"~/.gitconfig" = { source = "~/.gitconfig", mode = "symlink" }` where the file does not exist on the build machine (tilde not expanded as expected, path typo, or file only present on another host).

Common situations: Building images on CI where the dotfile exists only on a developer laptop; typos in relative paths resolved against the project dir; the file genuinely not created yet.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/5b93e9c0ea4a3866. Report an issue: GitHub.