jdx/mise · error

invalid cleanup target {}

Error message

invalid cleanup target {}

What it means

clean_apk_transients validates every cleanup target via reject_symlink_components: after stripping the rootfs prefix, each remaining path component must be a plain Normal component. Anything else (ParentDir, RootDir, Prefix — including paths that resolve through '..') is rejected as an invalid cleanup target, guarding against deleting outside the rootfs.

Source

Thrown at src/oci/packages.rs:557

    let cache = rootfs.join("var/cache/apk");
    let log = rootfs.join("var/log/apk.log");
    reject_symlink_components(rootfs, &cache)?;
    reject_symlink_components(rootfs, &log)?;
    remove_dir_children(&cache)?;
    remove_path(&log)
}

/// Reject a cleanup target when it or any path component below `rootfs` is a
/// symlink. OCI package layers are untrusted, so following one during cleanup
/// could remove files outside the layer root.
fn reject_symlink_components(rootfs: &Path, target: &Path) -> Result<()> {
    let relative = target
        .strip_prefix(rootfs)
        .wrap_err_with(|| format!("cleanup target {} is outside rootfs", target.display()))?;
    let mut current = rootfs.to_path_buf();
    for component in relative.components() {
        let std::path::Component::Normal(component) = component else {
            bail!("invalid cleanup target {}", target.display());
        };
        current.push(component);
        match fs::symlink_metadata(&current) {
            Ok(metadata) if metadata.file_type().is_symlink() => {
                bail!(
                    "refusing cleanup through symlink component {}",
                    current.display()
                );
            }
            Ok(_) => {}
            Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(()),
            Err(err) => {
                return Err(err)
                    .wrap_err_with(|| format!("reading metadata for {}", current.display()));
            }
        }
    }
    Ok(())

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Inspect the rootfs for unexpected '..'-containing or absolute transient paths left by apk
  2. Rebuild from a clean base image so apk's transient files are in expected locations
  3. If reproducible, report it — this usually indicates malformed path state, not user configuration

Example fix

null
Defensive patterns

Strategy: try-catch

Validate before calling

null

Try / catch

try {
  buildOciImage(config);
} catch (e) {
  if (String(e.message).includes("invalid cleanup target")) {
    // rebuild from a clean base image; report if reproducible
  } else throw e;
}

Prevention

When it happens

Trigger: clean_apk_transients computes a transient path (e.g. apk cache/db temp files) that normalizes to non-Normal components — typically because the stripped target still contains '..' or is a filesystem prefix — and reject_symlink_components bails.

Common situations: Corrupted or unexpected apk database paths inside the rootfs; symlinked or overlapping mount points causing odd relative paths; internal state bugs rather than user configuration errors.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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