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

  1. Ensure all git-odb work is complete and tasks joined before shutting down the workspace/runtime.
  2. Do not call acquire after the owning component has been closed; check lifecycle ordering.
  3. If you control shutdown, drop pending acquirers before closing the semaphore.
  4. 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

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


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/64bfe4c886957f34. Report an issue: GitHub.