{"record":{"id":"1db32dba116b0609","repo":"gastownhall/beads","slug":"timeout-waiting-for-proxy-on-s-safe-child-kill-f","errorCode":null,"errorMessage":"timeout waiting for proxy on %s; safe child kill failed: %w","messagePattern":"timeout waiting for proxy on (.+?); safe child kill failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/endpoint.go","lineNumber":402,"sourceCode":"\t\t\t\treturn Endpoint{}, fmt.Errorf(\n\t\t\t\t\t\"proxy child exited before becoming ready on explicitly configured port %d (see %s): %w\",\n\t\t\t\t\topts.Port, opts.LogFilePath, childErr,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn Endpoint{}, fmt.Errorf(\n\t\t\t\t\"proxy child exited before publishing its OS-assigned port (see %s): %w\",\n\t\t\t\topts.LogFilePath, childErr,\n\t\t\t)\n\t\tcase <-hard.C:\n\t\t\tif err := killSpawnedChild(child); err != nil {\n\t\t\t\treturn Endpoint{}, fmt.Errorf(\"hard timeout waiting for proxy on %s; safe child kill failed: %w\", describeSpawnPort(opts.Port), err)\n\t\t\t}\n\t\t\treturn Endpoint{}, fmt.Errorf(\"hard timeout (%s) waiting for proxy on %s\", spawnReadyHardTimeout, describeSpawnPort(opts.Port))\n\t\tcase <-poll.C:\n\t\t}\n\t\tif time.Now().After(deadline) {\n\t\t\tif err := killSpawnedChild(child); err != nil {\n\t\t\t\treturn Endpoint{}, fmt.Errorf(\"timeout waiting for proxy on %s; safe child kill failed: %w\", describeSpawnPort(opts.Port), err)\n\t\t\t}\n\t\t\treturn Endpoint{}, fmt.Errorf(\"timeout waiting for proxy to become ready on %s\", describeSpawnPort(opts.Port))\n\t\t}\n\t}\n}\n\n// describeSpawnPort renders a requested spawn port for wait/timeout\n// messages: 0 is the default OS-assigned path, not a literal \"port 0\".\nfunc describeSpawnPort(port int) string {\n\tif port == 0 {\n\t\treturn \"its OS-assigned port\"\n\t}\n\treturn fmt.Sprintf(\"port %d\", port)\n}\n\ntype spawnedProxyChild struct {\n\tcmd    *exec.Cmd\n\tdone   <-chan error","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/endpoint.go#L384-L420","documentation":"Returned by spawnAndHandoff when the caller-provided deadline passes and the proxy is still not ready, and then the safe kill of the child fails. It layers a process-management error on top of the startup timeout, meaning an orphan child may remain.","triggerScenarios":"spawnAndHandoff's poll loop passes time.Now().After(deadline) (the caller's deadline passed to GetCreateDatabaseProxyServerEndpoint) with the child still unready, and killSpawnedChild errors — e.g. the process vanished between check and signal or signal permission was denied.","commonSituations":"Tight caller deadline on a slow cold start; parent and child in different containers/UIDs blocking the kill; PID-namespace mismatch causing ESRCH; test environments with aggressive process reaping.","solutions":["Look for an orphaned db-proxy-child (pgrep) and clean it up so it doesn't hold the lock or port.","Increase the deadline passed to GetCreateDatabaseProxyServerEndpoint to accommodate cold-start time.","Diagnose why the kill failed (permissions, namespaces) before retrying to avoid accumulating zombies.","Retry the start after cleanup; the next attempt should adopt or spawn cleanly."],"exampleFix":"// before: fixed short deadline on slow storage\nep, err := GetCreateDatabaseProxyServerEndpoint(root, opts) // deadline := time.Now().Add(2*time.Second)\n// after: deadline scaled to environment\nep, err := GetCreateDatabaseProxyServerEndpoint(root, opts) // deadline := time.Now().Add(30*time.Second) on NFS/large DBs","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"ep, err := GetCreateDatabaseProxyServerEndpoint(rootDir, opts)\nif err != nil && strings.Contains(err.Error(), \"timeout waiting for proxy on\") {\n    // child kill also failed; ensure no orphan remains before retry\n    exec.Command(\"pkill\", \"-f\", \"db-proxy-child --root \"+rootDir).Run()\n    ep, err = GetCreateDatabaseProxyServerEndpoint(rootDir, opts)\n}","preventionTips":["Pass a generous deadline matching real cold-start duration","Keep caller and child in the same UID/PID namespace so the kill succeeds","Clean up orphaned children after any timeout before retrying","Avoid tight deadlines in slow-storage or high-load environments"],"tags":["go","process-spawn","timeout","deadline","process-kill"],"backgroundTag":"startup-timeout","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}