{"record":{"id":"ed12fa27a3340009","repo":"gastownhall/beads","slug":"proxy-child-exited-before-becoming-ready-on-explic","errorCode":null,"errorMessage":"proxy child exited before becoming ready on explicitly configured port %d (see %s): %w","messagePattern":"proxy child exited before becoming ready on explicitly configured port (.+?) \\(see (.+?)\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/endpoint.go","lineNumber":384,"sourceCode":"\t\t\t\treturn Endpoint{}, ierr\n\t\t\t} else if interrupted {\n\t\t\t\treturn Endpoint{}, fmt.Errorf(\"%w for %s\", errStartInterrupted, rootDir)\n\t\t\t}\n\t\t\tif childErr == nil {\n\t\t\t\tchildErr = errors.New(\"child exited without reporting an error\")\n\t\t\t}\n\t\t\t// A LockHeldExitCode exit is a lost spawn race, not a listen\n\t\t\t// failure; any other exit gets the child's log path so the real\n\t\t\t// error (listen, backend start, ...) is findable.\n\t\t\tvar exitErr *exec.ExitError\n\t\t\tif errors.As(childErr, &exitErr) && exitErr.ExitCode() == LockHeldExitCode {\n\t\t\t\treturn Endpoint{}, fmt.Errorf(\n\t\t\t\t\t\"proxy child lost the proxy.lock spawn race for %s: %w\",\n\t\t\t\t\trootDir, childErr,\n\t\t\t\t)\n\t\t\t}\n\t\t\tif opts.Port != 0 {\n\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)","sourceCodeStart":366,"sourceCodeEnd":402,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/endpoint.go#L366-L402","documentation":"Returned by spawnAndHandoff when the spawned proxy child exits before it became ready, and the caller explicitly configured a non-zero port via OpenOpts.Port. The child's own exit error is wrapped, and the error points at the child's log file because the real cause (port already in use, backend start failure, etc.) is recorded there.","triggerScenarios":"GetCreateDatabaseProxyServerEndpoint is called with OpenOpts.Port set to a specific value and the child dies during startup — e.g. bind fails because the port is occupied, the Dolt backend fails to start, or the child crashes on config loading.","commonSituations":"Config file pins a proxy port that another service already uses; leftover process from a previous run still listening on the pinned port; SELinux/firewall or container restrictions blocking the bind; bad --dolt-bin path crashing the backend.","solutions":["Read the child log at opts.LogFilePath — the wrapped error and log contain the actual startup failure.","Check whether the configured port is already in use (lsof/ss) and free it or pick a different port.","Set OpenOpts.Port to 0 to let the OS assign a free port and publish it via the discovery record.","Verify the backend prerequisites (dolt binary path, config file) before spawning."],"exampleFix":"// before: fixed port collides with an existing service\nopts := OpenOpts{Port: 8337, LogFilePath: logPath}\n// after: let the OS pick a free port and rely on port publication\nopts := OpenOpts{Port: 0, LogFilePath: logPath}","handlingStrategy":"validation","validationCode":"if opts.Port != 0 {\n    ln, err := net.Listen(\"tcp\", fmt.Sprintf(\"127.0.0.1:%d\", opts.Port))\n    if err != nil { return fmt.Errorf(\"configured port %d unavailable: %w\", opts.Port, err) }\n    ln.Close()\n}","typeGuard":null,"tryCatchPattern":"ep, err := GetCreateDatabaseProxyServerEndpoint(rootDir, opts)\nif err != nil && strings.Contains(err.Error(), \"exited before becoming ready on explicitly configured port\") {\n    log.Printf(\"proxy failed on port %d; see %s\", opts.Port, opts.LogFilePath)\n    opts.Port = 0 // fall back to OS-assigned port\n    ep, err = GetCreateDatabaseProxyServerEndpoint(rootDir, opts)\n}","preventionTips":["Leave Port at 0 (OS-assigned) unless a fixed port is required","Before pinning a port, check it is free with net.Listen","Always set LogFilePath so child failures are diagnosable","Kill stale proxies from previous runs before reusing a port"],"tags":["go","process-spawn","port-conflict","startup-failure"],"backgroundTag":"child-process-exited-before-ready","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}