multica-ai/multica · error
could not start the local login callback server (used to rec
Error message
could not start the local login callback server (used to receive the browser sign-in); a firewall or another process may be blocking local ports: %w
What it means
Browser login starts a temporary local HTTP server on a random port to receive the OAuth callback; net.Listen("tcp4", bindAddr+":0") binds it. If the bind fails (address in use on the resolved bind addr, firewall blocking loopback listeners, no IPv4 stack) the flow cannot start and returns this wrapped error.
Source
Thrown at server/cmd/multica/cmd_auth.go:251
return v4
}
return local.IP
}
func runAuthLoginBrowser(cmd *cobra.Command) error {
serverURL := resolveHumanServerURL(cmd)
appURL := resolveAppURL(cmd)
flagHost := callbackHostFlagValue(cmd)
callbackHost, bindAddr := resolveCallbackBinding(flagHost, serverURL, appURL, detectOutboundIP)
// Pin to "tcp4" — a bare "tcp" on macOS can produce an IPv6-only socket
// that IPv4 clients (including browsers resolving localhost → 127.0.0.1)
// cannot reach. The callback URL is always an IPv4 literal or hostname,
// so an IPv4 listener is what the browser actually needs.
listener, err := net.Listen("tcp4", bindAddr+":0")
if err != nil {
return fmt.Errorf("could not start the local login callback server (used to receive the browser sign-in); a firewall or another process may be blocking local ports: %w", err)
}
defer listener.Close()
port := listener.Addr().(*net.TCPAddr).Port
callbackURL := fmt.Sprintf("http://%s:%d/callback", callbackHost, port)
// Generate a random state parameter for CSRF protection.
stateBytes := make([]byte, 16)
if _, err := rand.Read(stateBytes); err != nil {
return fmt.Errorf("failed to generate state: %w", err)
}
state := hex.EncodeToString(stateBytes)
loginURL := fmt.Sprintf("%s/login?cli_callback=%s&cli_state=%s", appURL, url.QueryEscape(callbackURL), url.QueryEscape(state))
// Channel to receive the JWT from the browser callback.
jwtCh := make(chan string, 1)
errCh := make(chan error, 1)View on GitHub (pinned to 2c0912b6ec)
Solutions
- Use token login which needs no listener: `multica login --token <PAT>`
- Pin the callback host flag to loopback (e.g. --callback-host 127.0.0.1) so binding targets localhost
- Temporarily allow the CLI to listen on loopback in endpoint firewall policy
- If in a sandbox without networking, perform login on the host and mount/copy the profile config
Example fix
# before (bind on detected outbound IP fails) multica login # after multica login --callback-host 127.0.0.1 # or skip the listener entirely multica login --token mul_ABCdef123...
Defensive patterns
Strategy: fallback
Validate before calling
# cheap pre-check that loopback IPv4 binding works
python3 - <<'EOF' || echo 'loopback bind blocked — use token login'
import socket
s=socket.socket(socket.AF_INET); s.bind(('127.0.0.1',0)); s.close()
EOF Prevention
- Pin --callback-host to 127.0.0.1 in restricted environments
- Keep token login as the scripted fallback when the listener cannot bind
When it happens
Trigger: A firewall (corporate endpoint protection) blocking bind on the resolved callback host; the outbound-IP detection path resolving to an address that cannot be bound; IPv4 disabled/unavailable in the environment (some containers); unusually restrictive sandbox denying socket creation.
Common situations: Locked-down corporate laptops; container/agent sandboxes with no network or IPv4-only restrictions; SSH sessions where detectOutboundIP picks an unroutable bind address.
Related errors
- failed to generate state: %w
- local server error: %w
- timed out waiting for authentication
- failed to save config: %w
- no input
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/8a98599eabfbf9ab.
Report an issue: GitHub.