{"record":{"id":"46a981064f4becfe","repo":"gastownhall/beads","slug":"errstartinterrupted","errorCode":"errStartInterrupted","errorMessage":"proxy startup interrupted by concurrent shutdown","messagePattern":"proxy startup interrupted by concurrent shutdown","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/storage/dbproxy/proxy/endpoint.go","lineNumber":169,"sourceCode":"\t}\n}\n\ntype adoptionResult struct {\n\tstatus   adoptionStatus\n\tendpoint Endpoint\n\tpidfile  *pidfile.PidFile\n\terr      error\n}\n\ntype spawnMarker struct {\n\tSchema      int    `json:\"schema\"`\n\tPID         int    `json:\"pid\"`\n\tBirth       string `json:\"birth\"`\n\tStopEpoch   string `json:\"stop_epoch\"`\n\tStartedUnix int64  `json:\"started_unix\"`\n}\n\nvar errStartInterrupted = errors.New(\"proxy startup interrupted by concurrent shutdown\")\n\nfunc PickFreePort() (int, error) {\n\t// The managed proxy no longer uses this bind-close allocator: its child\n\t// binds port 0 and publishes the kernel-assigned port. The remaining\n\t// production caller allocates the Dolt config port; that race requires\n\t// the managed-config ownership/retry contract deferred to the PR-C RFC.\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tport := ln.Addr().(*net.TCPAddr).Port\n\t_ = ln.Close()\n\treturn port, nil\n}\n\nfunc GetCreateDatabaseProxyServerEndpoint(rootDir string, opts OpenOpts) (Endpoint, error) {\n\tif err := opts.Backend.Validate(); err != nil {\n\t\treturn Endpoint{}, fmt.Errorf(\"OpenOpts.Backend: %w\", err)","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/endpoint.go#L151-L187","documentation":"errStartInterrupted signals that a proxy start/spawn was aborted because a concurrent shutdown advanced the stop epoch while startup was in flight. GetCreateDatabaseDatabaseProxyServerEndpoint/spawnAndHandoff/ListenAndServe return it (wrapped with the workspace dir) so the caller knows the failure is a race, not a listen error.","triggerScenarios":"Calling GetCreateDatabaseProxyServerEndpoint while another goroutine/process calls Stop, advancing stopEpoch during spawnAndHandoff or ListenAndServe startup wait.","commonSituations":"Rapid start/stop cycles in tests or restart loops; a supervisor issuing shutdown while an initialization request is still being served; concurrent CLI invocations sharing a workspace.","solutions":["Retry the start after the shutdown completes — the error is transient by design","Serialize start/stop with a workspace-level lock or single supervisor goroutine","Check errors.Is(err, errStartInterrupted) and distinguish from real listen failures before alerting"],"exampleFix":"// before\nreturn endpoint.GetCreateDatabaseProxyServerEndpoint(ctx, rootDir)\n// after\nep, err := endpoint.GetCreateDatabaseProxyServerEndpoint(ctx, rootDir)\nif err != nil && errors.Is(err, endpoint.ErrStartInterrupted) {\n    time.Sleep(500 * time.Millisecond) // let shutdown finish\n    return endpoint.GetCreateDatabaseProxyServerEndpoint(ctx, rootDir)\n}","handlingStrategy":"retry","validationCode":"// check stop epoch before starting\nif epoch, err := readStopEpoch(rootDir); err == nil && epochChanged(epoch) { return errors.New(\"shutdown in progress\") }","typeGuard":"func isStartInterrupted(err error) bool { return errors.Is(err, endpoint.ErrStartInterrupted) }","tryCatchPattern":"ep, err := GetCreateDatabaseProxyServerEndpoint(ctx, root)\nif errors.Is(err, endpoint.ErrStartInterrupted) {\n    <-shutdownDone\n    return GetCreateDatabaseProxyServerEndpoint(ctx, root)\n}","preventionTips":["Serialize start and stop operations per workspace","Avoid rapid start/stop cycles","Use a single supervisor to coordinate lifecycle"],"tags":["concurrency","startup","race","shutdown"],"backgroundTag":"startup-interrupted-concurrent-shutdown","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}