{"record":{"id":"ba940f8af1ab57c2","repo":"gastownhall/beads","slug":"start-proxy-child-w","errorCode":null,"errorMessage":"start proxy child: %w","messagePattern":"start proxy child: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/endpoint.go","lineNumber":529,"sourceCode":"\treleased = true\n\tlock.Unlock()\n\tbeforeProxyChildStart()\n\n\t// GH#4634: same hazard as the direct sql-server spawn, one hop further\n\t// out. The proxy child is detached and long-lived, and it starts the\n\t// sql-server itself, so a caller's non-CLOEXEC descriptor would otherwise\n\t// be inherited twice over and pinned for the proxy's whole lifetime.\n\tif leaked := fdhygiene.MarkInheritedCloexec(); len(leaked) > 0 {\n\t\t// debug.Logf, not log.Printf: this fires in normal operation whenever\n\t\t// the caller's environment leaves any fd open, and the parent's stderr\n\t\t// may be parsed script output.\n\t\tdebug.Logf(\"dbproxy: marked %d inherited fd(s) close-on-exec before starting proxy child: %v\", len(leaked), leaked)\n\t}\n\n\tif err := cmd.Start(); err != nil {\n\t\t_ = logFile.Close()\n\t\t_ = clearOwnSpawnMarker(rootDir, marker)\n\t\treturn nil, fmt.Errorf(\"start proxy child: %w\", err)\n\t}\n\n\tdone := make(chan error, 1)\n\tgo func() {\n\t\twaitErr := cmd.Wait()\n\t\t_ = logFile.Close()\n\t\tdone <- waitErr\n\t\tclose(done)\n\t}()\n\n\t// Theoretical race: the birth token is captured after Start, so the child\n\t// could exit and its PID be recycled before Capture runs, making the\n\t// handle describe the unrelated replacement. The window is a few\n\t// milliseconds against OS PID-reuse latency, and the OS has no primitive\n\t// to atomically capture identity at spawn, so this is accepted and\n\t// documented rather than defended.\n\tvar handle *procid.Handle\n\tchildBirth, captureErr := procid.Capture(cmd.Process.Pid)","sourceCodeStart":511,"sourceCodeEnd":547,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/endpoint.go#L511-L547","documentation":"This error is wrapped in forkExecChild (called from spawnAndHandoff) when exec.Command.Start() fails to launch the detached dbproxy child process. cmd.Start() performs fork/exec and also fails if the binary is missing, not executable, or the process cannot be created; the log file and spawn marker are cleaned up before returning.","triggerScenarios":"spawnAndHandoff attempts to start the proxy child after releasing proxy.lock; cmd.Start() fails because the current executable (self) cannot be fork/exec'd or system process limits are exhausted.","commonSituations":"The bd binary was deleted or replaced (upgraded in place) between detection and spawn; EAGAIN from hitting RLIMIT_NPROC / cgroup process limits; tmpdir or exec resources unavailable; read-only or noexec filesystem preventing exec; permission denied on the binary.","solutions":["Verify the running binary still exists and is executable (ls -l /proc/self/exe; re-run bd after reinstalling)","Check process limits: ulimit -u, cgroup pids.max; raise or free processes","Check disk space and filesystem mount flags (noexec/read-only) for the executable and log file directory","Retry the command; if persistent, inspect the underlying error (os.StartProcess / fork errno) printed with %w"],"exampleFix":"// before\nif err := cmd.Start(); err != nil {\n    return nil, fmt.Errorf(\"start proxy child: %w\", err)\n}\n// after\nif err := cmd.Start(); err != nil {\n    var le *os.LinkError\n    if errors.As(err, &le) {\n        return nil, fmt.Errorf(\"start proxy child (binary %q, errno %v): %w\", le.Name, le.Err, err)\n    }\n    return nil, fmt.Errorf(\"start proxy child: %w\", err)\n}","handlingStrategy":"retry","validationCode":"self, _ := os.Executable()\nif fi, err := os.Stat(self); err != nil || fi.Mode()&0o111 == 0 {\n    return fmt.Errorf(\"cannot spawn proxy: executable %q missing or not executable\", self)\n}","typeGuard":null,"tryCatchPattern":"spawned, err := spawnAndHandoff(root)\nif err != nil && strings.HasPrefix(err.Error(), \"start proxy child:\") {\n    var le *os.LinkError\n    if errors.As(err, &le) { /* inspect le.Err: EACCES/EAGAIN/ENOENT */ }\n    // wait briefly and retry once\n}","preventionTips":["Avoid replacing the binary while bd is running (install upgrades atomically via rename)","Keep process limits (ulimit -u, cgroup pids.max) comfortably above concurrent bd usage","Ensure the workspace log file directory exists and is writable before spawning","Keep the binary on a filesystem without noexec/read-only restrictions"],"tags":["process-spawn","exec","go"],"backgroundTag":"process-spawn-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}