{"record":{"id":"295f824e806e555f","repo":"xai-org/grok-build","slug":"unmount-err","errorCode":null,"errorMessage":"unmount {}: {err}","messagePattern":"unmount (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-fast-worktree/src/overlay/snapshot.rs","lineNumber":573,"sourceCode":"        let err = std::io::Error::last_os_error();\n        bail!(\"mount overlay at {}: {err}\", target.display());\n    }\n\n    Ok(())\n}\n\n/// Unmount a filesystem (lazy/detach to avoid EBUSY).\nfn unmount_overlay(target: &Path) -> Result<()> {\n    use std::ffi::CString;\n\n    let c_target =\n        CString::new(target.as_os_str().as_encoded_bytes()).context(\"target path not C-safe\")?;\n\n    // SAFETY: c_target is a valid CString.\n    let rc = unsafe { libc::umount2(c_target.as_ptr(), libc::MNT_DETACH) };\n    if rc != 0 {\n        let err = std::io::Error::last_os_error();\n        bail!(\"unmount {}: {err}\", target.display());\n    }\n\n    Ok(())\n}\n\n/// Delete a btrfs subvolume/snapshot (delegates to shared btrfs module).\nfn delete_btrfs_snapshot(path: &Path) -> Result<()> {\n    crate::btrfs::snapshot::delete_snapshot(path)\n}\n\n/// Write metadata JSON to the worktree base dir for crash recovery.\n///\n/// Written to `<wt_base>/.fast-worktree-meta.json` (next to `upper/` and\n/// `work/` dirs), NOT inside the overlay. This ensures the metadata is\n/// always readable from the btrfs filesystem regardless of overlay mount state.\nfn write_metadata(\n    wt_base: &Path,\n    snapshot_root: &Path,","sourceCodeStart":555,"sourceCodeEnd":591,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-fast-worktree/src/overlay/snapshot.rs#L555-L591","documentation":"unmount_overlay lazily detaches a btrfs/mount overlay snapshot with umount2(MNT_DETACH). When the kernel returns a non-zero exit code, the raw OS error (last_os_error) is wrapped with the target path and bailed out. This means the kernel refused (or could not complete) the detach of the mount point at `target`.","triggerScenarios":"Calling cleanup_orphaned_overlay_snapshots -> unmount_overlay for a target path that is not currently a mount point (EINVAL/ENOENT), is busy (EBUSY because a process holds a file/dir open inside it), or requires privileges not available (EPERM when not root / lacking CAP_SYS_ADMIN).","commonSituations":"Running the cleanup as a non-root user; a leaked file descriptor or shell cd'd into the snapshot keeps it busy; the snapshot was already unmounted by another process (stale bookkeeping) so umount2 returns EINVAL; NFS/overlay races leaving the mount in a partially-detached state.","solutions":["Check the target is actually a mount point (e.g. /proc/self/mounts or findmnt) before calling; skip/treat already-unmounted as success.","Run as root or with CAP_SYS_ADMIN — umount2 requires privileges.","Find and stop processes holding the mount open (fuser -vm <target> / lsof +f -- <target>) and retry.","Since MNT_DETACH is lazy, a persistent EBUSY may need a plain retry loop or a stronger umount after closing holders."],"exampleFix":"// before: unconditional unmount, fails on non-mount targets\nunmount_overlay(&snapshot.target)?;\n// after: skip targets that are not mounted\nif is_mount_point(&snapshot.target) {\n    unmount_overlay(&snapshot.target)?;\n}","handlingStrategy":"validation","validationCode":"fn is_mount_point(p: &Path) -> bool {\n    let st = std::fs::metadata(p).map(|m| m.dev()).ok();\n    let parent_st = p.parent().and_then(|p| std::fs::metadata(p).ok()).map(|m| m.dev());\n    match (st, parent_st) {\n        (Some(a), Some(b)) => a != b,\n        _ => false,\n    }\n}\nif !is_mount_point(&target) { skip_or_log(); }","typeGuard":null,"tryCatchPattern":"match unmount_overlay(&target) {\n    Err(e) if e.to_string().contains(\"Invalid argument\") => {\n        tracing::debug!(\"already unmounted: {target:?}\");\n    }\n    Err(e) => return Err(e.context(format!(\"unmount {target:?} failed\"))),\n    Ok(()) => {}\n}","preventionTips":["Run cleanup under root or a systemd unit with CAP_SYS_ADMIN","Check findmnt before unmounting; treat 'not mounted' as success","Close processes holding files in the snapshot (leaked fds, shell cwd)","Retry once on EBUSY — MNT_DETACH may need a second pass"],"tags":["linux","filesystem","mount","permissions"],"backgroundTag":"umount-failed-ebusy","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}