{"record":{"id":"412559617052b51f","repo":"charmbracelet/crush","slug":"failed-to-start-oauth-callback-listener-all-candi","errorCode":null,"errorMessage":"failed to start OAuth callback listener: all candidate ports in use","messagePattern":"failed to start OAuth callback listener: all candidate ports in use","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/oauth/mcp/handler.go","lineNumber":149,"sourceCode":"\t// first free candidate, just long enough to learn which is open. Either\n\t// way the chosen port is baked into the redirect URI below and pinned on\n\t// the receiver, so bindLocked always rebinds the SAME port. The probe is\n\t// not a reservation — another process can take the port before the first\n\t// real login — but a busy port then fails loudly rather than silently\n\t// binding a port the redirect URI does not point at.\n\tport := callbackPort\n\tif port == 0 {\n\t\tlc := &net.ListenConfig{}\n\t\tfor _, p := range callbackPorts {\n\t\t\tprobe, err := lc.Listen(context.Background(), \"tcp\", fmt.Sprintf(\"localhost:%d\", p))\n\t\t\tif err == nil {\n\t\t\t\t_ = probe.Close()\n\t\t\t\tport = p\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif port == 0 {\n\t\t\treturn nil, errors.New(\"failed to start OAuth callback listener: all candidate ports in use\")\n\t\t}\n\t}\n\treceiver.fixedPort = port\n\n\tredirectURL := fmt.Sprintf(\"http://localhost:%d%s\", port, callbackPath)\n\n\th := &Handler{\n\t\treceiver:       receiver,\n\t\tserverURL:      serverURL,\n\t\topenURL:        browser.OpenURL,\n\t\tinteractive:    interactive,\n\t\tonTokenRefresh: onTokenRefresh,\n\t}\n\treceiver.handler = h\n\n\t// newTokenSource is the SDK hook invoked once after a successful code\n\t// exchange. The token it hands us is brand new, so persist it right\n\t// away, then wrap the source so later refreshes persist on change. The","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/oauth/mcp/handler.go#L131-L167","documentation":"NewHandler could not bind its local HTTP callback listener: every candidate port was already in use. The handler needs a localhost port to receive the OAuth redirect, so without one it cannot start the authorization flow.","triggerScenarios":"Creating a new MCP OAuth handler when all candidate callback ports (including the fixed port when configured) fail to bind — typically because another process holds them.","commonSituations":"Running multiple crush instances simultaneously, a stale crashed process still holding the port, or a configured fixed callback port occupied by another service.","solutions":["Find and stop the process holding the port (lsof -i :<port> / ss -ltnp) or wait for it to exit.","Remove or change any configured fixed callback port so the handler can pick a free port from the candidate range.","Retry creating the handler after the conflicting process exits.","Run only one crush instance that performs MCP OAuth at a time."],"exampleFix":"// before\nhandler, err := oauth.NewHandler(cfg) // fails when fixed port is taken\n// after\nlistener, err := net.Listen(\"tcp\", \"localhost:0\")\nif err != nil { return err }\nlistener.Close() // probe a free port first, or drop fixedPort from cfg\ncfg.CallbackPort = 0\nhandler, err := oauth.NewHandler(cfg)","handlingStrategy":"fallback","validationCode":"ln, err := net.Listen(\"tcp\", \"localhost:0\")\nif err != nil { /* ports exhausted or networking broken */ }\nln.Close()","typeGuard":null,"tryCatchPattern":"handler, err := oauth.NewHandler(cfg)\nif err != nil && strings.Contains(err.Error(), \"ports in use\") {\n    freeConflictingPort()\n    handler, err = oauth.NewHandler(cfg)\n}","preventionTips":["Avoid configuring a fixed callback port shared by other services.","Limit concurrent crush instances doing OAuth at the same time.","Clean up stale listeners from crashed processes.","Probe a free port before constructing the handler."],"tags":["oauth","mcp","port-conflict","tcp-listener"],"backgroundTag":"address-in-use","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}