GitoxideLabs/gitoxide · error

Removal target ' ' must be contained in boundary

Error message

Removal target '{target}' must be contained in boundary '{boundary}'

What it means

gix-fs' bounded directory remover (`gix_fs::dir::remove::new`) deletes `target` but guarantees it never deletes anything at or above `boundary`. It validates up front that `target`'s path starts with `boundary`; if not, it refuses with `InvalidInput` rather than risk deleting outside the allowed region. Paths are not canonicalized for performance, so the check is lexical.

Solutions

  1. Ensure both paths share the same form (both absolute or both relative) and that `target` literally starts with `boundary`.
  2. Canonicalize both paths (`std::fs::canonicalize` or `gix_fs` helpers) before constructing the remover.
  3. Fix the caller logic that computes `target` so it is always inside `boundary`.

Example fix

// before: mixed forms fail the starts_with check
let r = gix_fs::dir::remove::DirRemover::new(Path::new("repo/objects/pack"), Path::new("/abs/repo"))?;

// after: canonicalize both first
let target = std::fs::canonicalize("repo/objects/pack")?;
let boundary = std::fs::canonicalize("/abs/repo")?;
let r = gix_fs::dir::remove::DirRemover::new(&target, &boundary)?;
Defensive patterns

Strategy: validation

Validate before calling

let target = std::fs::canonicalize(target)?;
let boundary = std::fs::canonicalize(boundary)?;
assert!(target.starts_with(&boundary), "target must be inside boundary");

Try / catch

let dir = gix_fs::dir::remove::DirRemover::new(&target, &boundary)
    .map_err(|e| anyhow::anyhow!("bad removal target: {e}"))?;

Prevention

When it happens

Trigger: Calling `DirRemover::new(target, boundary)` where `target` is not a lexical descendant of `boundary` — e.g. non-canonicalized paths like `repo/./objects` vs `repo`, or a target from a different tree.

Common situations: Mixing relative and absolute paths; passing symlinked or `..`-containing paths that were never canonicalized; a bug computing the cleanup target during pack/odb garbage collection.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/62bd4522eea19485. Report an issue: GitHub.

Appendix: source

Thrown at gix-fs/src/dir/remove.rs:21

/// A special iterator which communicates its operation through results where…
///
/// * `Some(Ok(removed_directory))` is yielded once or more success, followed by `None`
/// * `Some(Err(std::io::Error))` is yielded exactly once on failure.
pub struct Iter<'a> {
    cursor: Option<&'a Path>,
    boundary: &'a Path,
}

/// Construction
impl<'a> Iter<'a> {
    /// Create a new instance that deletes `target` but will stop at `boundary`, without deleting the latter.
    /// Returns an error if `boundary` doesn't contain `target`
    ///
    /// **Note** that we don't canonicalize the path for performance reasons.
    pub fn new(target: &'a Path, boundary: &'a Path) -> std::io::Result<Self> {
        if !target.starts_with(boundary) {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                format!(
                    "Removal target '{target}' must be contained in boundary '{boundary}'",
                    target = target.display(),
                    boundary = boundary.display()
                ),
            ));
        }
        let cursor = if target == boundary {
            None
        } else if target.exists() {
            Some(target)
        } else {
            None
        };
        Ok(Iter { cursor, boundary })
    }
}

View on GitHub (pinned to e73179060b)