{"record":{"id":"de2e9f0a9a1fcb38","repo":"xai-org/grok-build","slug":"not-a-git-repository-de2e9f","errorCode":null,"errorMessage":"not a git repository: {}","messagePattern":"not a git repository: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-workspace/src/session/git_gate.rs","lineNumber":645,"sourceCode":"\nfn forget_cached_roots(root: &Path) {\n    ROOT_CACHE.lock().retain(|_, entry| entry.root != root);\n}\n\nasync fn canonical_git_root(path: &Path) -> Result<PathBuf> {\n    let cwd = dunce::canonicalize(path).unwrap_or_else(|_| path.to_path_buf());\n    if let Some(root) = lookup_cached_root(&cwd) {\n        return Ok(root);\n    }\n\n    let probe = cwd.clone();\n    let discovered = tokio::task::spawn_blocking(move || discover_git_root(&probe))\n        .await\n        .map_err(|error| anyhow!(\"git discover task failed: {error}\"))?;\n    let discovered = match discovered {\n        GitDiscoveryResult::Found(root) => root,\n        GitDiscoveryResult::NotARepo => {\n            anyhow::bail!(\"not a git repository: {}\", path.display())\n        }\n        GitDiscoveryResult::DiscoveryFailed(error) => {\n            return Err(error).context(format!(\"git discover failed for {}\", path.display()));\n        }\n    };\n    let root = dunce::canonicalize(&discovered).unwrap_or(discovered);\n    store_cached_root(cwd, root.clone());\n    Ok(root)\n}\n\n#[cfg(test)]\n#[path = \"git_gate_tests.rs\"]\nmod tests;\n","sourceCodeStart":627,"sourceCodeEnd":659,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-workspace/src/session/git_gate.rs#L627-L659","documentation":"In git_gate's root-discovery path (spawn_blocking around discover_git_root), a NotARepo result bails with 'not a git repository: <path>'. This gate runs before operations are allowed, refusing to proceed when the probe path has no git repository ancestor.","triggerScenarios":"Invoking a gated session/workspace operation with a working directory outside any repo; the gate's probe walk reaches the filesystem root without finding .git.","commonSituations":"Running the tool from a scratch/temp directory; .git deleted or repo moved; passing a drive root or home dir as workspace; wrong cwd set by an IDE/runner.","solutions":["cd into (or configure the workspace to point at) a directory inside a git repository","Verify with `git rev-parse --show-toplevel` from the intended directory","Re-clone or restore the repo if .git is missing","Note the DiscoveryFailed arm instead wraps errors with 'git discover failed for <path>' context — this bail is specifically NotARepo"],"exampleFix":"// before\ngate.check(Path::new(\"/tmp/scratch\")).await?;\n// after\ngate.check(Path::new(\"/home/dev/project\")).await?; // path inside the repo","handlingStrategy":"validation","validationCode":"fn gate_precheck(cwd: &Path) -> anyhow::Result<()> {\n    let out = std::process::Command::new(\"git\").arg(\"-C\").arg(cwd).args([\"rev-parse\",\"--show-toplevel\"]).output()?;\n    if !out.status.success() { anyhow::bail!(\"launch from inside a git repository, not {}\", cwd.display()); }\n    Ok(())\n}","typeGuard":"fn has_git_ancestor(p: &Path) -> bool { p.ancestors().any(|a| a.join(\".git\").exists()) }","tryCatchPattern":"match gate.enter(cwd).await {\n    Err(e) if e.to_string().starts_with(\"not a git repository\") => {\n        eprintln!(\"switch to your project directory or set workspace path inside the repo\");\n        Err(e)\n    }\n    other => other,\n}","preventionTips":["Start sessions from within the repo (or resolve repo root at startup)","Validate workspace configuration paths point inside a repo","Watch for moved/deleted repos; re-clone if .git is gone","Prefer canonicalized paths before gate checks"],"tags":["git","repository","gate","validation"],"backgroundTag":"not-a-git-repository","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}