{"record":{"id":"f0ccdc34348c054c","repo":"gastownhall/beads","slug":"child-exited-without-reporting-an-error","errorCode":null,"errorMessage":"child exited without reporting an error","messagePattern":"child exited without reporting an error","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/endpoint.go","lineNumber":371,"sourceCode":"\t\tdiscovered := readAndDial(rootDir)\n\t\tif discovered.status == adoptionAdopted {\n\t\t\tif err := sweepOldQuarantines(rootDir, time.Now()); err != nil {\n\t\t\t\tlog.Printf(\"dbproxy: could not sweep old quarantined records in %s: %v\", rootDir, err)\n\t\t\t}\n\t\t\treturn discovered.endpoint, nil\n\t\t}\n\t\tif discovered.status == adoptionIOErr {\n\t\t\treturn Endpoint{}, fmt.Errorf(\"discover spawned proxy: %w\", discovered.err)\n\t\t}\n\t\tselect {\n\t\tcase childErr := <-child.done:\n\t\t\tif interrupted, ierr := stopEpochChanged(rootDir, stopEpoch); ierr != nil {\n\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(","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/endpoint.go#L353-L389","documentation":"This error replaces a nil child error when the managed proxy child process exits without reporting a failure, preventing an uninformative nil from masking a real startup problem. spawnAndHandoff raises it when the child died but wrote no error; the actual cause is then located via the child's log path (attached for non-LockHeldExitCode exits).","triggerScenarios":"Child proxy process exits during spawnAndHandoff with exit code other than LockHeldExitCode and without writing its error to the handoff channel or log.","commonSituations":"Child killed by a signal (OOM killer, external kill) before it could report; binary crash before logging; environment problems (bad PATH, missing Dolt binary) causing silent early exit.","solutions":["Inspect the child log path included in the wrapped error to find the real failure","Re-run the start after checking resources (memory, disk, binary availability)","If ExitCode is LockHeldExitCode, treat it as a lost spawn race and retry instead"],"exampleFix":"// before\nep, err := GetCreateDatabaseProxyServerEndpoint(ctx, root)\nif err != nil { return err }\n// after\nep, err := GetCreateDatabaseProxyServerEndpoint(ctx, root)\nif err != nil {\n    var ee *exec.ExitError\n    if errors.As(err, &ee) && ee.ExitCode() == LockHeldExitCode {\n        return GetCreateDatabaseProxyServerEndpoint(ctx, root) // lost race, retry\n    }\n    return fmt.Errorf(\"proxy spawn failed: %w\", err) // err mentions child log path\n}","handlingStrategy":"try-catch","validationCode":"if _, err := os.Stat(childLogPath); err == nil { // inspect log before retrying }","typeGuard":"func isSilentChildExit(err error) bool { return strings.Contains(err.Error(), \"child exited without reporting an error\") }","tryCatchPattern":"ep, err := GetCreateDatabaseProxyServerEndpoint(ctx, root)\nif err != nil {\n    var ee *exec.ExitError\n    if errors.As(err, &ee) && ee.ExitCode() == LockHeldExitCode { return retry() }\n    log.Errorf(\"proxy spawn failed (see child log in error): %v\", err)\n    return err\n}","preventionTips":["Keep child logs on disk and readable","Monitor for OOM kills and signal deaths of children","Handle LockHeldExitCode as a retryable lost race"],"tags":["process","spawn","child-process","diagnostics"],"backgroundTag":"child-exited-silently","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}