xai-org/grok-build · error

failed to delete BTRFS snapshot at {}: {}

Error message

failed to delete BTRFS snapshot at {}: {}

What it means

`delete_snapshot` runs `btrfs subvolume delete <path>`; if the command exits non-zero, it bails with this message including the path and btrfs's stderr. The btrfs tool refused to delete the subvolume snapshot.

Source

Thrown at crates/codegen/xai-fast-worktree/src/btrfs/snapshot.rs:331

}

/// Delete a BTRFS subvolume/snapshot.
pub fn delete_snapshot(path: &Path) -> Result<()> {
    let mut cmd = Command::new("btrfs");
    xai_tty_utils::detach_std_command(&mut cmd);
    cmd.stdin(Stdio::null());
    // Pass the path as OsStr (no lossy `to_str`) so a non-UTF-8 path can never
    // silently collapse to "." and delete the current directory's subvolume.
    let output = cmd
        .arg("subvolume")
        .arg("delete")
        .arg(path)
        .output()
        .with_context(|| "failed to execute btrfs subvolume delete command")?;

    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        bail!(
            "failed to delete BTRFS snapshot at {}: {}",
            path.display(),
            stderr.trim()
        );
    }

    Ok(())
}

/// Validate that `snapshot_path` is a safe target for a privileged
/// `btrfs subvolume delete`.
///
/// Guards against destroying an arbitrary subvolume (e.g. the live source repo,
/// or another session/user's snapshot) via a stale, confused, or planted
/// symlink or `*.btrfs-meta.json` entry. Returns `true` only when the path:
/// - contains no `..` component,
/// - is not itself a symlink (`lstat`, so a planted symlink can't redirect the
///   delete to a subvolume elsewhere),

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Read the stderr in the error message for btrfs's exact reason ('not a subvolume', 'device busy', 'permission denied').
  2. Ensure nothing uses the snapshot: unmount it, stop containers/worktrees holding it open, then retry.
  3. Verify the path is actually a btrfs subvolume with `btrfs subvolume show <path>`; delete plain directories with `rm -rf` instead.
  4. Run with sufficient privileges and confirm the `btrfs` tool exists in the environment.
  5. For recreation flows, handle this error by cleaning dependents first rather than retrying the same call blindly.

Example fix

// before
delete_snapshot(Path::new("/mnt/btrfs/snapshots/wt-42"))?; // busy: still mounted
// after
// ensure the snapshot is not mounted, then retry
run("umount /mnt/btrfs/snapshots/wt-42");
delete_snapshot(Path::new("/mnt/btrfs/snapshots/wt-42"))?;
Defensive patterns

Strategy: try-catch

Validate before calling

use std::process::Command;
use std::path::Path;
fn snapshot_deletable(p: &Path) -> bool {
    // must be a btrfs subvolume and not currently mounted
    let is_subvol = Command::new("btrfs").args(["subvolume", "show"]).arg(p)
        .output().map(|o| o.status.success()).unwrap_or(false);
    let not_mounted = !std::fs::read_to_string("/proc/mounts")
        .map(|m| m.contains(&p.to_string_lossy().to_string())).unwrap_or(true);
    is_subvol && not_mounted
}

Try / catch

match delete_snapshot(&path) {
    Err(e) if e.to_string().contains("failed to delete BTRFS snapshot") => {
        eprintln!("btrfs delete refused: {e}");
        // stop consumers / unmount, then retry once
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling `delete_snapshot` (directly or from `create_snapshot_with_symlink`'s recreate path) when the path isn't a btrfs subvolume, is busy (open files, mounted), or the user lacks privileges.

Common situations: Snapshot still mounted or referenced by a running worktree/container; target is a plain directory, not a subvolume; running without root/CAP_SYS_ADMIN; filesystem errors or read-only subvolume; `btrfs` binary missing in containers.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/7f555ee061eea125. Report an issue: GitHub.