{"record":{"id":"552e5ecad2098c96","repo":"xai-org/grok-build","slug":"pin-delete-requires-grove","errorCode":null,"errorMessage":"pin delete requires grove","messagePattern":"pin delete requires grove","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-fast-worktree/src/nfs/liveness.rs","lineNumber":529,"sourceCode":"    let mut buf = Vec::new();\n    Read::take(file, MAX_MARKER_BYTES.saturating_add(1))\n        .read_to_end(&mut buf)\n        .ok()?;\n    if buf.len() as u64 > MAX_MARKER_BYTES {\n        return None;\n    }\n    Some(buf)\n}\nfn pin_exists(source: &Path, worktree_id: &str) -> Result<bool> {\n    {\n        let _ = (source, worktree_id);\n        Ok(false)\n    }\n}\nfn delete_pin_ref_gated(source: &Path, worktree_id: &str) -> Result<()> {\n    {\n        let _ = (source, worktree_id);\n        anyhow::bail!(\"pin delete requires grove\")\n    }\n}\n#[cfg(feature = \"metadata\")]\npub fn identities_from_worktree_records(recs: &[crate::db::WorktreeRecord]) -> Vec<NfsIdentity> {\n    recs.iter()\n        .filter(|r| crate::worktree::is_grove_strategy(&r.creation_mode))\n        .map(|r| {\n            let grove = r\n                .metadata\n                .as_ref()\n                .and_then(|m| m.get(\"grove\").or_else(|| m.get(\"nfs\")));\n            let backing = grove\n                .and_then(|n| n.get(\"backing\"))\n                .and_then(|b| b.as_str())\n                .filter(|s| !s.is_empty())\n                .map(PathBuf::from);\n            let pin = grove\n                .and_then(|n| n.get(\"source_pin\"))","sourceCodeStart":511,"sourceCodeEnd":547,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-fast-worktree/src/nfs/liveness.rs#L511-L547","documentation":"delete_pin_ref_gated is the non-grove (feature-gated) stub for removing a pin reference. In this build, pin deletion is only implemented through the grove daemon, so the function unconditionally bails: any caller attempting a pin delete without grove support compiled in / without the daemon gets this error.","triggerScenarios":"Calling gc_orphan_pins (which invokes delete_pin_ref_gated) on a code path that is not gated to grove-managed worktrees — i.e. trying to delete an NFS pin reference outside the grove-managed flow.","commonSituations":"Running an orphan-pin GC while worktrees were created with a non-grove creation_mode; a build without the metadata/grove gating reaching the stub; callers porting old pin-cleanup code to the new gated API.","solutions":["Ensure pin deletion goes through the grove daemon path (gate gc_orphan_pins on grove strategy, e.g. filter records with is_grove_strategy)","Compile/enable the grove feature so the real implementation is used instead of the stub","Skip pin deletion for non-grove worktrees instead of calling the gated function"],"exampleFix":"// before\nfor id in orphans {\n    delete_pin_ref_gated(&source, id)?;\n}\n// after\nfor rec in recs.iter().filter(|r| crate::worktree::is_grove_strategy(&r.creation_mode)) {\n    delete_pin_ref_gated(&rec.source, &rec.worktree_id)?;\n}","handlingStrategy":"fallback","validationCode":"#[cfg(feature = \"metadata\")]\nlet is_grove = recs.iter().any(|r| crate::worktree::is_grove_strategy(&r.creation_mode));\nif !is_grove { return Ok(()); } // skip pin deletion","typeGuard":null,"tryCatchPattern":"match delete_pin_ref_gated(&source, id) {\n    Err(e) if e.to_string() == \"pin delete requires grove\" => {\n        // skip or route through the grove daemon path\n    }\n    r => r?,\n}","preventionTips":["Only call pin deletion for records with grove creation_mode","Build with the grove/metadata features enabled when pin management is needed","Wrap gated calls with cfg(feature) or a capability check"],"tags":["feature-gate","pin","gc","nfs"],"backgroundTag":"feature-not-enabled","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}