gastownhall/beads · error
timeout after %s waiting for server at %s
Error message
timeout after %s waiting for server at %s
What it means
waitForReady returns this error when it exhausts its polling deadline without ever observing a MySQL handshake greeting from the server at host:port. It probes with ProbeSQLServer every 500ms until the timeout; a TCP connect that succeeds without a greeting is explicitly not treated as ready. In Start this error is wrapped into the richer 'server started (PID %d) but not accepting connections' messages, but it can also surface directly wherever waitForReady is used.
Source
Thrown at internal/doltserver/doltserver.go:1856
// dolt sql-server process from interpreting probe closes as aborted MySQL
// handshakes and crashing (see gastownhall/beads#4132, #4133).
//
// A dial that succeeds but never greets (TCP listener accepting, MySQL
// engine not yet writing) is not treated as ready: this function keeps
// polling until either a greeting arrives or the deadline is reached.
func waitForReady(host string, port int, timeout time.Duration) error {
addr := net.JoinHostPort(host, strconv.Itoa(port))
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
greeted, err := ProbeSQLServer("tcp", addr, 500*time.Millisecond) //nolint:gosec // G704: addr is built from internal host+port, not user input
if err == nil && greeted {
return nil
}
time.Sleep(500 * time.Millisecond)
}
return fmt.Errorf("timeout after %s waiting for server at %s", timeout, addr)
}
// ensureDoltIdentity sets dolt global user identity from git config if not already set.
func ensureDoltIdentity() error {
// Check if dolt identity is already configured
nameCmd := exec.Command("dolt", "config", "--global", "--get", "user.name")
if out, err := nameCmd.Output(); err == nil && strings.TrimSpace(string(out)) != "" {
return nil // Already configured
}
// Try to get identity from git
gitName := "beads"
gitEmail := "beads@localhost"
if out, err := exec.Command("git", "config", "user.name").Output(); err == nil {
if name := strings.TrimSpace(string(out)); name != "" {
gitName = name
}View on GitHub (pinned to 71377f2769)
Solutions
- Read the server log (.beads/dolt-server.log) — a timeout here almost always hides a startup error or a slow init
- Allow more time for large databases by increasing the ready timeout (BEADS_DOLT_READY_TIMEOUT) and retrying
- If the log shows journal corruption, run 'bd doctor --fix' to back up and reinitialize
- Check host resources (memory/CPU/disk I/O) and container limits; free resources or raise limits
- Verify the probed host matches the bind host (127.0.0.1 vs 0.0.0.0) and that nothing else grabbed the port
Example fix
// before: huge database, default timeout too short // error: timeout after 15s waiting for server at 127.0.0.1:41017 // after: extend the readiness window // $ export BEADS_DOLT_READY_TIMEOUT=120s // $ bd dolt start
Defensive patterns
Strategy: retry
Validate before calling
// Pre-check: the port must be free and host bindable before waiting for readiness
ln, err := net.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(port)))
if err == nil { ln.Close() } // if bind succeeds here, a later timeout means the server itself is stuck
if err != nil {
return fmt.Errorf("port %d unavailable on %s", port, host)
} Try / catch
err := waitForReady(host, port, timeout) // or via doltserver.Start
if err != nil && strings.Contains(err.Error(), "timeout after") {
// bounded retry: one more start attempt with a larger window
time.Sleep(2 * time.Second)
if err := waitForReady(host, port, timeout*2); err != nil {
return fmt.Errorf("server never became ready at %s; check %s", addr, logPath)
}
} Prevention
- Increase BEADS_DOLT_READY_TIMEOUT for large databases or slow disks
- Guarantee adequate container memory/CPU for dolt sql-server
- Gracefully stop servers (bd dolt stop) to avoid journal corruption that stalls init
- Match probed host to the server's bind host (127.0.0.1 vs 0.0.0.0)
- Monitor the server log during startup instead of only watching the deadline
When it happens
Trigger: The probe loop times out because the dolt sql-server never sends a MySQL handshake: the process is hung during engine init (corrupt journal, huge database), is stuck before binding, or binds but the MySQL layer never starts writing — all before readyTimeout() elapses.
Common situations: Slow first startup on a very large database exceeding the default ready window; corrupt journal after unclean shutdown stalling engine init; resource-starved containers (OOM pressure, throttled CPU); server listening on a different interface than probed; stuck dolt process from a prior crash holding locks.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- server started (PID %d) but not accepting connections on por
- server started (PID %d) but not accepting connections on por
- errIdleTimeout
- server: NewDoltServer: failed to determine absolute path of
- server: NewDoltServer: failed to determine absolute path of
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/221a263e17a39aec.
Report an issue: GitHub.