{"record":{"id":"e737e024a66f0087","repo":"xai-org/grok-build","slug":"git-discover-task-failed-error","errorCode":null,"errorMessage":"git discover task failed: {error}","messagePattern":"git discover task failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-workspace/src/session/git_gate.rs","lineNumber":641,"sourceCode":"            cached_at: Instant::now(),\n        },\n    );\n}\n\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":623,"sourceCodeEnd":659,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-workspace/src/session/git_gate.rs#L623-L659","documentation":"Git root discovery runs in a blocking task (spawn_blocking(discover_git_root)). If the tokio runtime shuts down or the task is cancelled, .await returns a JoinError, which the library wraps as \"git discover task failed: {error}\". This is distinct from discovery itself failing — the discovery task never delivered a result.","triggerScenarios":"Calling the discovery entry point (git_gate.rs:641) while the tokio runtime is shutting down (task cancelled during drop), the runtime's blocking pool is saturated/killed, or the spawned task panics — the JoinError is then wrapped into this anyhow error.","commonSituations":"A server stopping while in-flight requests still attempt git discovery; tests tearing down their runtime before futures complete; blocking-pool exhaustion causing cancellation/panic in the discover task; panic inside discover_git_root (e.g. poisoned lock or unwraps) surfacing as a JoinError.","solutions":["Retry the operation on a healthy runtime — discovery is idempotent.","Ensure the tokio runtime outlives all in-flight calls (keep the Runtime/Arc of the service alive until shutdown completes, await your tasks before dropping the runtime).","If JoinError shows a panic, fix the panicking path in discover_git_root / guard against poisoned state.","In tests, use a common runtime handle per test and shutdown_timeout so tasks are not cancelled mid-flight."],"exampleFix":"// before\n{\n    let rt = tokio::runtime::Runtime::new()?;\n    rt.block_on(service.discover(&path))?;\n} // runtime dropped while other tasks still run -> JoinError\n// after\nlet rt = tokio::runtime::Runtime::new()?;\nrt.block_on(service.discover(&path))?;\nrt.shutdown_timeout(Duration::from_secs(10)); // let in-flight tasks finish","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// JoinError means the task never finished — safe to retry on a live runtime\nmatch discover().await {\n    Err(e) if e.to_string().contains(\"git discover task failed\") => {\n        tokio::time::sleep(Duration::from_millis(200)).await;\n        discover().await\n    }\n    other => other,\n}","preventionTips":["Keep the tokio runtime (and the service using it) alive until all in-flight operations complete.","Call runtime.shutdown_timeout instead of dropping the runtime abruptly.","In tests, share one runtime per test and await spawned tasks before teardown.","Fix panics inside discover_git_root — they surface to callers as this JoinError wrapper."],"tags":["git","tokio","runtime","task-cancellation"],"backgroundTag":"tokio-join-error","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}