gastownhall/beads · error
start control listener: %w
Error message
start control listener: %w
What it means
Wraps a failure from startControl(p.rootDir, ...), which brings up the Unix-domain-socket/TCP control listener used for identity handshake on the proxy. If the control listener cannot be created or bound, ListenAndServe aborts with this error.
Source
Thrown at internal/storage/dbproxy/proxy/server.go:254
}
if _, err := identity.WriteSecret(p.rootDir); err != nil {
return fmt.Errorf("write proxy secret: %w", err)
}
var identMu sync.RWMutex
identReply := identity.IdentReply{
Schema: pidfile.SchemaV2,
Role: pidfile.KindProxy,
DataPort: dataPort.Port,
}
control, err := startControl(p.rootDir, func() identity.IdentReply {
identMu.RLock()
defer identMu.RUnlock()
return identReply
})
if err != nil {
return fmt.Errorf("start control listener: %w", err)
}
defer func() { _ = control.Close() }()
p.stats.IncBackendStart()
if err := p.server.Start(ctx); err != nil {
// Start failed with no backend left running (Start cleans up its own
// failure), so there is no teardown to move off the lock; classifying
// the epoch-watcher cancellation just keeps the child's exit reason
// precise for the spawning parent.
if changed, cerr := stopEpochChanged(p.rootDir, p.stopEpoch); cerr == nil && changed {
return fmt.Errorf("%w for %s: stop epoch advanced during backend start (%v)", errStartInterrupted, p.rootDir, err)
}
return fmt.Errorf("start database server: %w", err)
}
if err := waitForServerReady(ctx, p.server, serverReadyTimeout); err != nil {
if changed, cerr := stopEpochChanged(p.rootDir, p.stopEpoch); cerr == nil && changed {
return abortInterruptedStart()View on GitHub (pinned to 71377f2769)
Solutions
- Remove any stale control socket file in rootDir and retry
- Check write permissions on rootDir for the proxy process
- Shorten the socket path (use a shorter rootDir) if hitting the Unix socket path length limit
- Check for another live proxy holding the control endpoint
Example fix
// before
// stale socket left behind, startControl fails
// after
if fi, err := os.Lstat(sockPath); err == nil && fi.Mode()&os.ModeSocket != 0 {
_ = os.Remove(sockPath) // clean stale socket before start
} Defensive patterns
Strategy: fallback
Validate before calling
// Pre-clean stale control sockets under rootDir
entries, _ := filepath.Glob(filepath.Join(rootDir, "*.sock"))
for _, s := range entries {
if fi, err := os.Lstat(s); err == nil && fi.Mode()&os.ModeSocket != 0 { os.Remove(s) }
} Try / catch
if err := p.ListenAndServe(ctx); err != nil {
if strings.Contains(err.Error(), "start control listener") {
// clean stale socket and retry once
os.RemoveAll(filepath.Join(rootDir, "control.sock"))
err = p.ListenAndServe(ctx)
}
if err != nil { log.Fatal(err) }
} Prevention
- Remove stale sockets on crash recovery paths
- Keep rootDir paths short to avoid Unix socket path-length limits
- Ensure only one control listener per rootDir
When it happens
Trigger: startControl fails because its socket path is unwritable, the socket file already exists, or its bind() fails due to permissions or address restrictions.
Common situations: Stale socket file left by a crashed proxy; rootDir permissions changed; socket path too long (>108 chars on Unix); another process bound the same control port/path.
Related errors
- allocating ephemeral port: %w
- bind %s: %w
- listen on %s: %w
- server: DoltServer.Dial: %w
- server: ExternalDoltServer.Dial: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a62ac187d4a40aeb.
Report an issue: GitHub.