{"record":{"id":"a62ac187d4a40aeb","repo":"gastownhall/beads","slug":"start-control-listener-w","errorCode":null,"errorMessage":"start control listener: %w","messagePattern":"start control listener: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/server.go","lineNumber":254,"sourceCode":"\t}\n\n\tif _, err := identity.WriteSecret(p.rootDir); err != nil {\n\t\treturn fmt.Errorf(\"write proxy secret: %w\", err)\n\t}\n\n\tvar identMu sync.RWMutex\n\tidentReply := identity.IdentReply{\n\t\tSchema:   pidfile.SchemaV2,\n\t\tRole:     pidfile.KindProxy,\n\t\tDataPort: dataPort.Port,\n\t}\n\tcontrol, err := startControl(p.rootDir, func() identity.IdentReply {\n\t\tidentMu.RLock()\n\t\tdefer identMu.RUnlock()\n\t\treturn identReply\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"start control listener: %w\", err)\n\t}\n\tdefer func() { _ = control.Close() }()\n\n\tp.stats.IncBackendStart()\n\tif err := p.server.Start(ctx); err != nil {\n\t\t// Start failed with no backend left running (Start cleans up its own\n\t\t// failure), so there is no teardown to move off the lock; classifying\n\t\t// the epoch-watcher cancellation just keeps the child's exit reason\n\t\t// precise for the spawning parent.\n\t\tif changed, cerr := stopEpochChanged(p.rootDir, p.stopEpoch); cerr == nil && changed {\n\t\t\treturn fmt.Errorf(\"%w for %s: stop epoch advanced during backend start (%v)\", errStartInterrupted, p.rootDir, err)\n\t\t}\n\t\treturn fmt.Errorf(\"start database server: %w\", err)\n\t}\n\n\tif err := waitForServerReady(ctx, p.server, serverReadyTimeout); err != nil {\n\t\tif changed, cerr := stopEpochChanged(p.rootDir, p.stopEpoch); cerr == nil && changed {\n\t\t\treturn abortInterruptedStart()","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/server.go#L236-L272","documentation":"Wraps a failure from startControl(p.rootDir, ...), which brings up the Unix-domain-socket/TCP control listener used for identity handshake on the proxy. If the control listener cannot be created or bound, ListenAndServe aborts with this error.","triggerScenarios":"startControl fails because its socket path is unwritable, the socket file already exists, or its bind() fails due to permissions or address restrictions.","commonSituations":"Stale socket file left by a crashed proxy; rootDir permissions changed; socket path too long (>108 chars on Unix); another process bound the same control port/path.","solutions":["Remove any stale control socket file in rootDir and retry","Check write permissions on rootDir for the proxy process","Shorten the socket path (use a shorter rootDir) if hitting the Unix socket path length limit","Check for another live proxy holding the control endpoint"],"exampleFix":"// before\n// stale socket left behind, startControl fails\n// after\nif fi, err := os.Lstat(sockPath); err == nil && fi.Mode()&os.ModeSocket != 0 {\n    _ = os.Remove(sockPath) // clean stale socket before start\n}","handlingStrategy":"fallback","validationCode":"// Pre-clean stale control sockets under rootDir\nentries, _ := filepath.Glob(filepath.Join(rootDir, \"*.sock\"))\nfor _, s := range entries {\n    if fi, err := os.Lstat(s); err == nil && fi.Mode()&os.ModeSocket != 0 { os.Remove(s) }\n}","typeGuard":null,"tryCatchPattern":"if err := p.ListenAndServe(ctx); err != nil {\n    if strings.Contains(err.Error(), \"start control listener\") {\n        // clean stale socket and retry once\n        os.RemoveAll(filepath.Join(rootDir, \"control.sock\"))\n        err = p.ListenAndServe(ctx)\n    }\n    if err != nil { log.Fatal(err) }\n}","preventionTips":["Remove stale sockets on crash recovery paths","Keep rootDir paths short to avoid Unix socket path-length limits","Ensure only one control listener per rootDir"],"tags":["network","unix-socket","control-listener","bind"],"backgroundTag":"socket-bind-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}