sipeed/picoclaw · error
unsupported login method %q for provider %q
Error message
unsupported login method %q for provider %q
What it means
Returned as HTTP 400 by POST /api/oauth/login when the method passes provider normalization but is not in that provider's method matrix (oauthProviderMethods). The matrix is strict: openai supports browser, device_code, token; anthropic supports only token; google-antigravity supports only browser. The method string is lowercased and trimmed, but must otherwise match exactly (underscore in "device_code", not hyphen).
Source
Thrown at web/backend/api/oauth.go:199
var req struct {
Provider string `json:"provider"`
Method string `json:"method"`
Token string `json:"token"`
}
if err = json.Unmarshal(body, &req); err != nil {
http.Error(w, fmt.Sprintf("invalid JSON: %v", err), http.StatusBadRequest)
return
}
provider, err := normalizeOAuthProvider(req.Provider)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
method := strings.ToLower(strings.TrimSpace(req.Method))
if !isOAuthMethodSupported(provider, method) {
http.Error(
w,
fmt.Sprintf("unsupported login method %q for provider %q", method, provider),
http.StatusBadRequest,
)
return
}
switch method {
case oauthMethodToken:
token := strings.TrimSpace(req.Token)
if token == "" {
http.Error(w, "token is required", http.StatusBadRequest)
return
}
cred := &auth.AuthCredential{
AccessToken: token,
Provider: provider,View on GitHub (pinned to 49183d7e8d)
Solutions
- Match the matrix exactly: openai → browser|device_code|token, anthropic → token, google-antigravity → browser.
- Read the "methods" array from GET /api/oauth/providers per provider and only render/offer those login options.
- Use the literal strings "browser", "device_code", "token" — note the underscore in device_code; the value is trimmed and lowercased but not otherwise normalized.
- If you believe a method should be supported (e.g. anthropic browser OAuth), it requires a backend change (oauthProviderMethods plus an oauthConfigForProvider entry), not a config tweak.
Example fix
// before
const method = provider === 'anthropic' ? 'device_code' : 'browser';
await fetch('/api/oauth/login', {method:'POST', body: JSON.stringify({provider, method})});
// -> 400 unsupported login method "device_code" for provider "anthropic"
// after
const supported = await getProviderMethods(provider); // from GET /api/oauth/providers
const method = supported.includes('device_code') ? 'device_code' : supported[0]; Defensive patterns
Strategy: validation
Validate before calling
const MATRIX = {
'openai': ['browser', 'device_code', 'token'],
'anthropic': ['token'],
'google-antigravity': ['browser'],
};
function assertMethod(provider, method) {
const m = String(method ?? '').trim().toLowerCase();
if (!MATRIX[provider]?.includes(m)) throw new Error(`unsupported login method ${JSON.stringify(method)} for provider ${JSON.stringify(provider)}`);
return m;
} Type guard
function isMethodSupported(provider, method) {
return (MATRIX[provider] ?? []).includes(String(method ?? '').trim().toLowerCase());
} Prevention
- Drive the login-method UI from the 'methods' array returned by GET /api/oauth/providers.
- Use the exact literals 'browser', 'device_code', 'token' — underscore, not hyphen.
- Remember anthropic is token-only and google-antigravity is browser-only.
When it happens
Trigger: POST /api/oauth/login with {"provider":"anthropic","method":"browser"} (anthropic has no browser flow), or {"provider":"openai","method":"device-code"} (hyphen instead of underscore), or {"provider":"google-antigravity","method":"device_code"} (antigravity is browser-only).
Common situations: Frontend that shows the same three login buttons for every provider; renaming method strings during an API migration; assuming device_code works everywhere because it works for OpenAI.
Related errors
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/6af4c35be0527e6e.
Report an issue: GitHub.