gastownhall/beads · error
listener not ready after %s: %w
Error message
listener not ready after %s: %w
What it means
waitReady polled the server's listener by dialing it repeatedly; after startReadyTimeout (30s) elapsed without a successful connection, it returns the last dial error wrapped with "listener not ready after 30s". Start() then tears the child down and returns this via error 2405.
Source
Thrown at internal/storage/dbproxy/server/doltserver.go:335
}
func (s *DoltServer) waitReady(ctx context.Context) error {
deadline := time.Now().Add(startReadyTimeout)
for {
if s.egCtx.Err() != nil {
return errors.New("dolt sql-server exited before listener became ready")
}
dctx, dcancel := context.WithTimeout(ctx, startReadyDialTimeout)
conn, err := s.Dial(dctx)
dcancel()
if err == nil {
_ = conn.Close()
return nil
}
if time.Now().After(deadline) {
return fmt.Errorf("listener not ready after %s: %w", startReadyTimeout, err)
}
select {
case <-ctx.Done():
return ctx.Err()
case <-s.egCtx.Done():
return errors.New("dolt sql-server exited before listener became ready")
case <-time.After(startReadyPollInterval):
}
}
}
func (s *DoltServer) Stop(ctx context.Context) error {
gcErr := s.runShutdownGC(ctx)
if gcErr != nil {
gcErr = fmt.Errorf("server: DoltServer.Stop: %w", gcErr)
}
View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the server log for the child's fatal output (port in use, config parse error).
- Cross-check host/port/socket in the dolt server config YAML against what your client DSN uses.
- Free the configured port (kill the conflicting process) or change the port in the config.
- Retry on a less loaded system; if starts are consistently >30s, this is a scaling/IO problem to address on the host.
Example fix
// before // config.yaml: host: 10.0.0.5, port: 3307 (client dials localhost:3307) // after // config.yaml: host: 127.0.0.1, port: 3307 — match host/port with client DSN
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the dolt config's host/port pair is reachable and free.
h, p := cfgHost(configPath), cfgPort(configPath)
conn, err := net.DialTimeout("tcp", net.JoinHostPort(h, strconv.Itoa(p)), time.Second)
if err == nil {
conn.Close()
return fmt.Errorf("something is already listening on %s:%d", h, p)
} Try / catch
if err := srv.Start(ctx); err != nil && strings.Contains(err.Error(), "listener not ready after") {
// inspect log file content for the real dial/bind failure, then surface it
tail, _ := os.ReadFile(logPath)
return fmt.Errorf("server not ready: %v; dolt log tail: %s", err, lastLines(tail, 20))
} Prevention
- Make client DSN host/port exactly match the dolt server config (socket vs TCP).
- Reserve the port for the server; pick dynamic ports in tests.
- Check firewall/SELinux policies allow local loopback connections.
- Always capture dolt's stdout/stderr into a log file for post-mortem.
When it happens
Trigger: Every dial (TCP to host:port, or socket) fails for the full 30s window — dolt never bound the listener because it crashed, is misconfigured (wrong host/port/socket in the YAML), or is catastrophically slow to start.
Common situations: Config binds to a different host than Dial checks; port already taken by another process so dolt exits; SELinux/firewall blocking local connections; extremely slow disk making dolt's bootstrap exceed 30s.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- server not reachable: %w
- git ls-remote %s failed: %s: %w
- max retries (%d) exceeded: %w
- request failed: %w
- identity: set control deadline: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/f4f547410ff9bd54.
Report an issue: GitHub.