{"record":{"id":"fe8ab9ef939741ed","repo":"gastownhall/beads","slug":"proxy-child-exited-before-publishing-its-os-assign","errorCode":null,"errorMessage":"proxy child exited before publishing its OS-assigned port (see %s): %w","messagePattern":"proxy child exited before publishing its OS-assigned port \\(see (.+?)\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/endpoint.go","lineNumber":389,"sourceCode":"\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)\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}","sourceCodeStart":371,"sourceCodeEnd":407,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/endpoint.go#L371-L407","documentation":"Returned by spawnAndHandoff when the spawned proxy child exits before it publishes its OS-assigned port in the discovery record (i.e. OpenOpts.Port was 0). The error includes the child's log path so the underlying startup failure can be diagnosed.","triggerScenarios":"GetCreateDatabaseProxyServerEndpoint with OpenOpts.Port == 0 where the child dies during startup before writing its assigned port: backend start failure, database open error, config parse failure, or a crash.","commonSituations":"Missing or corrupt dolt database in the workspace; invalid --config file; wrong DoltBinPath; disk-full or permission problems preventing the child from writing its record; recent bd version change breaking the child invocation.","solutions":["Inspect the child log at opts.LogFilePath for the real startup error.","Verify the workspace database opens cleanly (run the backend directly) and the config file parses.","Check DoltBinPath points to a working dolt binary and that the workspace directory is writable.","Re-run after clearing stale discovery/quarantine records under the workspace root if a previous crash left them behind."],"exampleFix":"// before: silently relying on defaults hides backend misconfig\nep, err := GetCreateDatabaseProxyServerEndpoint(root, OpenOpts{})\n// after: supply a log path so failures are diagnosable, and verify the backend first\nep, err := GetCreateDatabaseProxyServerEndpoint(root, OpenOpts{LogFilePath: filepath.Join(root, \"proxy.log\"), DoltBinPath: verifiedDoltPath})","handlingStrategy":"validation","validationCode":"if _, err := os.Stat(doltBin); err != nil {\n    return fmt.Errorf(\"dolt binary missing: %w\", err)\n}\nif fi, err := os.Stat(rootDir); err != nil || !fi.IsDir() { return fmt.Errorf(\"bad workspace %q\", rootDir) }","typeGuard":null,"tryCatchPattern":"ep, err := GetCreateDatabaseProxyServerEndpoint(rootDir, opts)\nif err != nil && strings.Contains(err.Error(), \"exited before publishing its OS-assigned port\") {\n    log, rerr := os.ReadFile(opts.LogFilePath)\n    log.Printf(\"proxy spawn failed; child log: %s (%v)\", log, rerr)\n}","preventionTips":["Set LogFilePath on every open so the real failure is in the child log","Validate the backend prerequisites (dolt binary, database, config) before spawning","Keep the workspace on local writable storage","Upgrade bd consistently so parent and child binaries match"],"tags":["go","process-spawn","startup-failure","port-publication"],"backgroundTag":"child-process-exited-before-ready","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}