xai-org/grok-build · error
git object database semaphore closed
Error message
git object database semaphore closed
What it means
OdbHandle::acquire waits, up to a configured timeout, for a permit on a global semaphore that limits concurrent access to the git object database. The Tokio semaphore's acquire_owned() returned Err(Closed), meaning the semaphore has been permanently shut down (all handles closed / dropped by the runtime teardown). This is an invariant violation, not a capacity issue.
Source
Thrown at crates/codegen/xai-grok-workspace/src/git_odb.rs:66
#[must_use]
pub fn new(permits: usize, acquire_wait: Duration) -> Self {
Self {
inner: Arc::new(OdbLimiterInner {
sem: Arc::new(Semaphore::new(permits.max(1))),
acquire_wait,
}),
}
}
pub async fn acquire(&self) -> Result<OdbPermit> {
match tokio::time::timeout(
self.inner.acquire_wait,
self.inner.sem.clone().acquire_owned(),
)
.await
{
Ok(Ok(permit)) => Ok(OdbPermit { _permit: permit }),
Ok(Err(_)) => Err(anyhow!("git object database semaphore closed")),
Err(_) => Err(anyhow!(
"timed out waiting {}s for git object database permit",
self.inner.acquire_wait.as_secs()
)),
}
}
pub fn try_acquire(&self) -> Option<OdbPermit> {
self.inner
.sem
.clone()
.try_acquire_owned()
.ok()
.map(|permit| OdbPermit { _permit: permit })
}
}
#[cfg(test)]View on GitHub (pinned to bc7f02eddd)
Solutions
- Ensure all git-odb work is complete and tasks joined before shutting down the workspace/runtime.
- Do not call acquire after the owning component has been closed; check lifecycle ordering.
- If you control shutdown, drop pending acquirers before closing the semaphore.
- Re-create the handle/workspace rather than reusing a closed one.
Example fix
// before: task keeps acquiring after shutdown
let handle = tokio::spawn(async move { odb.acquire().await?.read_commit(oid).await });
drop(workspace); // closes semaphore
// after
let handle = tokio::spawn(async move { odb.acquire().await?.read_commit(oid).await });
let _ = handle.await; // join before dropping workspace
drop(workspace); Defensive patterns
Strategy: try-catch
Validate before calling
// Check lifecycle before acquiring
if workspace.is_shutdown() {
anyhow::bail!("workspace already shut down; git odb unavailable");
} Try / catch
match odb.acquire().await {
Ok(permit) => { /* use permit */ }
Err(e) if e.to_string() == "git object database semaphore closed" => {
eprintln!("odb shut down; aborting work gracefully");
// stop background work, do not retry
}
Err(e) => return Err(e),
} Prevention
- Join all background tasks before dropping/shutting down the workspace
- Never spawn tasks holding an OdbHandle that can outlive the handle
- Treat semaphore-closed as terminal: do not retry, fix shutdown ordering
When it happens
Trigger: Calling acquire (directly or through workspace/odb operations) after the owning structure was shut down and its semaphore was closed — e.g. acquiring a permit during process/runtime shutdown, or using an OdbHandle whose shared inner state was closed elsewhere.
Common situations: Background tasks outliving the workspace and trying to read git objects during shutdown; calling acquire on a handle after close/shutdown was invoked; spawn-then-drop ordering bugs in tests.
Related errors
- timed out waiting {}s for git object database permit
- upload queue closed
- auth entry '{scope_key}' not found while persisting refreshe
- process scope already closed; fetch killed{}
- Task panicked: {}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/64bfe4c886957f34.
Report an issue: GitHub.