github/github-mcp-server · warning
%w: %w
Error message
%w: %w
What it means
beginPKCE wraps any listenCallback failure as '%w: %w' with the sentinel errCallbackBind (declared at flow.go:19 as 'OAuth callback listener could not bind'). The sentinel lets begin() distinguish genuine bind failures from unrelated preparation errors (e.g. randomState failing): only errors.Is(err, errCallbackBind) escalate to the fatal fixed-port message (115); everything else falls through to device flow. So this message itself appears in logs ('PKCE flow unavailable, falling back to device flow') when the port was random.
Source
Thrown at internal/oauth/flow.go:84
}
// beginPKCE prepares the authorization-code + PKCE flow. It binds the callback
// server and selects the most secure available display channel: browser
// auto-open, then URL elicitation, then a tool-response message. On a headless
// host with a random callback port it diverts to device flow, whose redirect
// does not depend on reaching this machine's localhost.
func (m *Manager) beginPKCE(prompter Prompter) (*flowPlan, error) {
state, err := randomState()
if err != nil {
return nil, err
}
verifier := oauth2.GenerateVerifier()
// Bind to all interfaces only inside a container, where the published port
// is delivered via eth0 rather than loopback. Native runs stay on loopback.
listener, err := listenCallback(m.config.CallbackPort, m.inDocker())
if err != nil {
return nil, fmt.Errorf("%w: %w", errCallbackBind, err)
}
if m.inDocker() {
// Inside a container the callback binds all interfaces so the published
// port is reachable, which also exposes it to the container network.
// Publishing to loopback only (e.g. -p 127.0.0.1:%d:%d) keeps the
// authorization code off the network.
m.logger.Warn(fmt.Sprintf("OAuth callback is listening on all container interfaces; publish it to loopback only (e.g. -p 127.0.0.1:%d:%d) so the authorization code is not exposed on your network", m.config.CallbackPort, m.config.CallbackPort))
}
cs := newCallbackServer(listener, state)
oc := m.oauth2Config(cs.redirect)
authURL := oc.AuthCodeURL(state, oauth2.S256ChallengeOption(verifier))
run := func(ctx context.Context) (*oauth2.Token, error) {
code, err := cs.wait(ctx)
if err != nil {
return nil, err
}View on GitHub (pinned to 0ea1f775a7)
Solutions
- If you saw the fatal variant (115), follow its guidance; if this appears as a device-flow fallback 'reason', a random bind failed — usually transient, retry
- Widen the ephemeral range / reduce TIME_WAIT pressure (net.ipv4.ip_local_port_range, tcp_tw_reuse) on socket-churn hosts
- Specify a fixed high port with --oauth-callback-port to avoid ephemeral-range contention (accepting the 115 escalation if it is taken)
- Simply retry the login — random-port collisions vanish on re-attempt
Defensive patterns
Strategy: fallback
Type guard
errors.Is(err, errCallbackBind)
Try / catch
// inside begin(): only bind failures on a fixed port escalate; everything else falls back
if m.config.CallbackPort != 0 && errors.Is(err, errCallbackBind) { return nil, fatal }
m.logger.Info("PKCE flow unavailable, falling back to device flow", "reason", err) Prevention
- On hosts with heavy socket churn, widen ip_local_port_range or set a fixed high callback port
- Retry login once on a random-port bind collision before investigating
When it happens
Trigger: listenCallback fails at internal/oauth/flow.go:82-85 — port occupied (with CallbackPort==0, the OS-chosen random port is exhausted on ephemeral-range-heavy systems), ephemeral range exhausted ('cannot assign requested address'), or bind permission denied. With a fixed port, this error is rewrapped into 115; with a random port (0) or non-Docker fallback paths, it is logged as the reason for device-flow fallback.
Common situations: Massively many TIME_WAIT/ephemeral sockets exhausting the local port range; container net.ipv4.ip_local_port_range too small; a random port that collided with a busy service; heavy test churn binding thousands of listeners.
Related errors
- starting callback listener on %s: %w
- callback server: %w
- OAuth callback port %d is not available; another process may
- requesting device code: %w
- App not connected
AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15).
Data as JSON: /api/errors/1c78b8b80bb7828b.
Report an issue: GitHub.