gastownhall/beads · error
clear proxy spawn marker: %w
Error message
clear proxy spawn marker: %w
What it means
After acquiring proxy.lock, ListenAndServe clears the proxy spawn marker written by the spawning parent. If clearSpawnMarkerAfterLock fails, startup aborts with this wrapped error, since a leftover marker could make a future stop/wait misbehave.
Source
Thrown at internal/storage/dbproxy/proxy/server.go:130
if lockfile.IsLocked(err) {
return ErrLockHeld
}
return fmt.Errorf("acquire %s: %w", LockFileName, err)
}
// proxy.lock is held for the proxy's whole lifetime, but a doomed start
// must be able to release it early (before its backend teardown) without
// the deferred release double-unlocking. Only the main goroutine touches
// this.
lockHeld := true
releaseLock := func() {
if lockHeld {
lockHeld = false
lock.Unlock()
}
}
defer releaseLock()
if err := clearSpawnMarkerAfterLock(p.rootDir); err != nil {
return fmt.Errorf("clear proxy spawn marker: %w", err)
}
// Fast-abort: a concurrent `bd dolt stop` advances the stop epoch before
// waiting (briefly) for proxy.lock, so an epoch that moved between the
// spawning parent's read and this child taking the lock dooms this start.
// Abort before opening any listener or booting the backend: the stopper
// then observes a free lock within milliseconds instead of after a full
// doomed boot-and-teardown cycle.
if changed, err := stopEpochChanged(p.rootDir, p.stopEpoch); err != nil {
return fmt.Errorf("check proxy stop epoch after acquiring %s: %w", LockFileName, err)
} else if changed {
return fmt.Errorf("%w for %s: stop epoch advanced before startup", errStartInterrupted, p.rootDir)
}
logPath := filepath.Join(p.rootDir, LogFileName)
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600) // #nosec G304 -- logPath is derived from operator-supplied config, not untrusted request input
if err != nil {
return fmt.Errorf("open proxy log %q: %w", logPath, err)View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped %w cause and fix filesystem permissions on the marker file
- Manually remove the stale spawn marker and restart the proxy
- Ensure only one spawner/child pair operates per rootDir
Defensive patterns
Strategy: retry
Validate before calling
marker := filepath.Join(rootDir, markerFile)
if _, err := os.Stat(marker); err == nil {
if err := os.Remove(marker); err != nil { return err } // fail fast, pre-check
} Try / catch
if err := p.ListenAndServe(ctx); err != nil {
if strings.Contains(err.Error(), "spawn marker") { /* check perms, retry */ }
return err
} Prevention
- Ensure the daemon user owns rootDir state files
- Avoid spawning two children for the same rootDir
- Manually clear stale markers after crashed starts
When it happens
Trigger: Calling ListenAndServe when clearSpawnMarkerAfterLock(p.rootDir) fails — marker file exists but cannot be removed (permissions, race with another remover, filesystem error).
Common situations: Read-only or full filesystem; rootDir ownership changed; two children spawned concurrently both racing to clear the marker.
Related errors
- multiple .doltcfg directories detected
- resolving .doltcfg directory: %w
- opening log file: %w
- resolving managed sql-server config path: %w
- writing managed sql-server config: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/00f0a49ce0793438.
Report an issue: GitHub.