router-for-me/CLIProxyAPI · error

port %d is already in use

Error message

port %d is already in use

What it means

The Codex OAuth callback server (mirrors the Claude one) refuses to start because its configured callback port is already bound. The pre-start isPortAvailable probe failed, so the login flow aborts before opening the browser. Distinct from the async failure (error 199), which happens after the probe passes.

Source

Thrown at internal/auth/codex/oauth_server.go:79

}

// Start starts the OAuth callback server.
// It sets up the HTTP handlers for the callback and success endpoints,
// and begins listening on the specified port.
//
// Returns:
//   - error: An error if the server fails to start
func (s *OAuthServer) Start() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.running {
		return fmt.Errorf("server is already running")
	}

	// Check if port is available
	if !s.isPortAvailable() {
		return fmt.Errorf("port %d is already in use", s.port)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/auth/callback", s.handleCallback)
	mux.HandleFunc("/success", s.handleSuccess)

	s.server = &http.Server{
		Addr:         fmt.Sprintf(":%d", s.port),
		Handler:      mux,
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	s.running = true

	// Start server in goroutine
	go func() {
		if err := s.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Identify and stop the holder: `lsof -i :<port>` / `ss -ltnp`, kill the stale process.
  2. Avoid concurrent logins; wait for the first to finish.
  3. Retry login with a different --oauth-callback-port.
  4. Move the callback port away from ports your other services use.

Example fix

# before
./cli-proxy-api login --provider codex   # port held by stale process

# after
kill $(lsof -t -i :1455)
./cli-proxy-api login --provider codex
Defensive patterns

Strategy: validation

Validate before calling

ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil { return fmt.Errorf("codex callback port %d busy; pass --oauth-callback-port", port) }
ln.Close()

Try / catch

if err := server.Start(); err != nil && strings.Contains(err.Error(), "already in use") {
    port = pickFreePort(); rebuildServer(port)
}

Prevention

When it happens

Trigger: Running Codex login while another CLIProxyAPI instance, a previous crashed process, or an unrelated service holds the callback port; two concurrent logins sharing the default port.

Common situations: Stale background process from an earlier login still bound; parallel logins from two terminals; the callback port colliding with a commonly used dev port; leftover container holding the port in CI.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/ba607db47859ba3c. Report an issue: GitHub.