{"record":{"id":"221a263e17a39aec","repo":"gastownhall/beads","slug":"timeout-after-s-waiting-for-server-at-s","errorCode":null,"errorMessage":"timeout after %s waiting for server at %s","messagePattern":"timeout after (.+?) waiting for server at (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/doltserver/doltserver.go","lineNumber":1856,"sourceCode":"// dolt sql-server process from interpreting probe closes as aborted MySQL\n// handshakes and crashing (see gastownhall/beads#4132, #4133).\n//\n// A dial that succeeds but never greets (TCP listener accepting, MySQL\n// engine not yet writing) is not treated as ready: this function keeps\n// polling until either a greeting arrives or the deadline is reached.\nfunc waitForReady(host string, port int, timeout time.Duration) error {\n\taddr := net.JoinHostPort(host, strconv.Itoa(port))\n\tdeadline := time.Now().Add(timeout)\n\n\tfor time.Now().Before(deadline) {\n\t\tgreeted, err := ProbeSQLServer(\"tcp\", addr, 500*time.Millisecond) //nolint:gosec // G704: addr is built from internal host+port, not user input\n\t\tif err == nil && greeted {\n\t\t\treturn nil\n\t\t}\n\t\ttime.Sleep(500 * time.Millisecond)\n\t}\n\n\treturn fmt.Errorf(\"timeout after %s waiting for server at %s\", timeout, addr)\n}\n\n// ensureDoltIdentity sets dolt global user identity from git config if not already set.\nfunc ensureDoltIdentity() error {\n\t// Check if dolt identity is already configured\n\tnameCmd := exec.Command(\"dolt\", \"config\", \"--global\", \"--get\", \"user.name\")\n\tif out, err := nameCmd.Output(); err == nil && strings.TrimSpace(string(out)) != \"\" {\n\t\treturn nil // Already configured\n\t}\n\n\t// Try to get identity from git\n\tgitName := \"beads\"\n\tgitEmail := \"beads@localhost\"\n\n\tif out, err := exec.Command(\"git\", \"config\", \"user.name\").Output(); err == nil {\n\t\tif name := strings.TrimSpace(string(out)); name != \"\" {\n\t\t\tgitName = name\n\t\t}","sourceCodeStart":1838,"sourceCodeEnd":1874,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/doltserver/doltserver.go#L1838-L1874","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: huge database, default timeout too short\n// error: timeout after 15s waiting for server at 127.0.0.1:41017\n// after: extend the readiness window\n// $ export BEADS_DOLT_READY_TIMEOUT=120s\n// $ bd dolt start","handlingStrategy":"retry","validationCode":"// Pre-check: the port must be free and host bindable before waiting for readiness\nln, err := net.Listen(\"tcp\", net.JoinHostPort(host, strconv.Itoa(port)))\nif err == nil { ln.Close() } // if bind succeeds here, a later timeout means the server itself is stuck\nif err != nil {\n    return fmt.Errorf(\"port %d unavailable on %s\", port, host)\n}","typeGuard":null,"tryCatchPattern":"err := waitForReady(host, port, timeout) // or via doltserver.Start\nif err != nil && strings.Contains(err.Error(), \"timeout after\") {\n    // bounded retry: one more start attempt with a larger window\n    time.Sleep(2 * time.Second)\n    if err := waitForReady(host, port, timeout*2); err != nil {\n        return fmt.Errorf(\"server never became ready at %s; check %s\", addr, logPath)\n    }\n}","preventionTips":["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"],"tags":["go","timeout","tcp","mysql-handshake","server-startup"],"backgroundTag":"server-ready-timeout","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}