charmbracelet/crush · error
failed to create OAuth handler for mcp %q: %w
Error message
failed to create OAuth handler for mcp %q: %w
What it means
After resolving URL and credentials, createTransport constructs the OAuth handler via mcpoauth.NewHandler (handling PRM discovery, token storage, registration). Any failure from that constructor is wrapped as 'failed to create OAuth handler for mcp %q'. The URL is trailing-slash-normalized beforehand for PRM discovery compatibility.
Source
Thrown at internal/agent/tools/mcp/init.go:1109
clientID, err := resolver.ResolveValue(m.OAuthClientID)
if err != nil {
return nil, nil, fmt.Errorf("oauth_client_id: %w", err)
}
clientSecret, err := resolver.ResolveValue(m.OAuthClientSecret)
if err != nil {
return nil, nil, fmt.Errorf("oauth_client_secret: %w", err)
}
preregistered = &oauth.OAuthClient{
ClientID: strings.TrimSpace(clientID),
ClientSecret: strings.TrimSpace(clientSecret),
}
}
// Normalize trailing slash for PRM discovery compatibility.
normalizedURL := strings.TrimSuffix(url, "/")
oauthHandler, oauthErr := mcpoauth.NewHandler(name, normalizedURL, m.OAuthToken, preregistered, tokenSaver, mcpoauth.IsInteractive(ctx), m.OAuthCallbackPort)
if oauthErr != nil {
return nil, nil, fmt.Errorf("failed to create OAuth handler for mcp %q: %w", name, oauthErr)
}
authURLs.Set(name, oauthHandler)
return &mcp.StreamableClientTransport{
Endpoint: url,
OAuthHandler: oauthHandler,
}, oauthHandler, nil
}
headers, err := m.ResolvedHeaders(resolver)
if err != nil {
return nil, nil, err
}
client := &http.Client{
Transport: &headerRoundTripper{
headers: headers,
},
}
return &mcp.StreamableClientTransport{View on GitHub (pinned to 7944b8e522)
Solutions
- Read the wrapped cause for the underlying NewHandler failure
- Verify the MCP server's /.well-known/oauth-protected-resource metadata is reachable
- Check the OAuth callback port is free (or unset to use the default) and writable token cache dir
- Clear cached OAuth state/token files for this server and retry the auth flow
Example fix
// before: port fixed and already in use mcp api type http url 'https://api.example.com/mcp' oauth oauth_callback_port 8080 // after: let the handler pick a free port mcp api type http url 'https://api.example.com/mcp' oauth
Defensive patterns
Strategy: fallback
Validate before calling
resp, err := http.Get(strings.TrimSuffix(serverURL, "/") + "/.well-known/oauth-protected-resource")
if err != nil || resp.StatusCode != 200 {
return errors.New("OAuth metadata discovery unavailable; check server URL and connectivity")
} Try / catch
if _, err := mcpoauth.NewHandler(name, url, token, nil, saver, interactive, port); err != nil {
// clear cached token state and retry once, else surface the wrapped error
} Prevention
- Verify the server exposes valid /.well-known OAuth metadata before enabling oauth
- Choose a free callback port or omit the port option
- Ensure the token cache directory is writable (set HOME in CI)
- Clear stale OAuth caches after server-side registration changes
When it happens
Trigger: mcpoauth.NewHandler returning an error: invalid OAuth token cache file, malformed server metadata/PRM discovery configuration, invalid callback port, or internal client registration failures.
Common situations: Unwritable token persistence location (HOME not set in CI); OAuth callback port already in use or out of allowed range; MCP server's well-known OAuth metadata unreachable or malformed; stale cached client registration after server-side changes.
Related errors
- oauth token source: %w
- failed to get MCP auth URL: status code %d
- interactive OAuth authorization required
- failed to start OAuth callback listener: all candidate ports
- OAuth callback listener closed
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/be92bf3a77e08d73.
Report an issue: GitHub.