gastownhall/beads · error
timeout after %s waiting for bootstrap lock (another bootstr
Error message
timeout after %s waiting for bootstrap lock (another bootstrap may be running)
What it means
The bootstrap lock is held by another process. acquireBootstrapLock polls with non-blocking flock every 100ms until the timeout deadline passes; if the lock is never released within that window, it gives up with this timeout error, closing its own file handle first. It indicates a concurrent bootstrap is running (or a stale holder).
Source
Thrown at internal/storage/dolt/bootstrap.go:250
// Try to acquire lock with non-blocking flock and polling.
deadline := time.Now().Add(timeout)
for {
err := lockfile.FlockExclusiveNonBlocking(f)
if err == nil {
// Lock acquired - update modification time for stale detection
return f, nil
}
if !lockfile.IsLocked(err) {
// Unexpected error (not contention)
_ = f.Close() // Best effort cleanup on error path
return nil, fmt.Errorf("failed to acquire bootstrap lock: %w", err)
}
if time.Now().After(deadline) {
_ = f.Close() // Best effort cleanup on error path
return nil, fmt.Errorf("timeout after %s waiting for bootstrap lock (another bootstrap may be running)", timeout)
}
// Wait briefly before retrying
time.Sleep(100 * time.Millisecond)
}
}
// releaseBootstrapLock releases the bootstrap lock and removes the lock file
func releaseBootstrapLock(f *os.File, lockPath string) {
if f != nil {
_ = lockfile.FlockUnlock(f) // Best effort: unlock may fail if fd is bad
_ = f.Close() // Best effort cleanup
}
// Clean up lock file
_ = os.Remove(lockPath) // Best effort cleanup of lock file
}
View on GitHub (pinned to 71377f2769)
Solutions
- Wait for the other bootstrap to finish, then retry — the timeout message tells you another bootstrap may be running
- Find and stop the process holding the lock: `lsof .beads/bootstrap.lock` (or equivalent) and kill stale `bd`/`dolt` processes
- Increase spacing between concurrent agent sessions, or serialize bootstrap calls behind your own coordination
- If the holder is dead but the flock persists (unusual), delete the lock file and retry
Example fix
// before bd bootstrap & bd bootstrap & # second one times out // after bd bootstrap && bd doctor # serialize; or wait for first to exit before retrying
Defensive patterns
Strategy: retry
Validate before calling
// detect a concurrent bootstrap before starting one // Unix: fuser .beads/bootstrap.lock 2>/dev/null && echo 'bootstrap in progress' // or check for running processes: pgrep -f 'bd bootstrap'
Try / catch
if err := bd.BootstrapFromRemoteWithDB(ctx, url, target); err != nil {
if strings.Contains(err.Error(), "timeout after") {
// wait/poll for the other bootstrap to finish, then retry;
// if no holder exists, remove stale lock and retry
}
} Prevention
- Serialize bootstrap calls across agent sessions/CI with your own coordination
- Check for running `bd`/`dolt` processes before starting bootstrap
- Use lsof/fuser to identify stale lock holders after crashed runs
- Avoid launching parallel bd bootstrap commands on the same database
When it happens
Trigger: Two `bd bootstrap` invocations run concurrently on the same database (e.g. two agent sessions, a CI job overlapping local work), or a previous bootstrap process crashed/hung while still holding the flock on the lock file until the deadline expires.
Common situations: Parallel agent workflows each calling `bd bootstrap`; a long-running `bd` process (server mode) holding the lock; a stuck dolt subprocess from an earlier failed bootstrap.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- timeout waiting for cache lock on %s
- timeout waiting for proxy on %s
- timeout clearing spawn marker %s
- timeout (%s) waiting for spawn marker %s; wait for the in-pr
- timeout (%s) acquiring %s after inspecting pid %d at %s; sto
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/b53b1671b9596488.
Report an issue: GitHub.