decolua/9router · error · Error
data.error
Error message
data.error
What it means
In the OAuthModal component, after the user submits the manual authorization code the client POSTs to the provider's manual-code exchange endpoint; a non-2xx response causes `throw new Error(data.error)`. On success the modal shows the success step and calls onSuccess. As with the Kiro modal, a body lacking `error` yields message `undefined`.
Source
Thrown at src/shared/components/OAuthModal.js:89
// Exchange tokens
const exchangeTokens = useCallback(async (code, state) => {
if (!authData) return;
try {
const res = await fetch(`/api/oauth/${provider}/exchange`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
code,
redirectUri: authData.redirectUri,
codeVerifier: authData.codeVerifier,
state,
...(oauthMeta ? { meta: oauthMeta } : {}),
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
setStep("success");
onSuccess?.();
} catch (err) {
setError(err.message);
setStep("error");
}
}, [authData, provider, onSuccess, oauthMeta]);
const completeXaiManualCode = useCallback(async (code) => {
if (!authData?.state) return;
try {
const res = await fetch("/api/oauth/xai/manual-code", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code, state: authData.state }),
});
const data = await res.json();View on GitHub (pinned to 90b52e06ff)
Solutions
- Regenerate the auth URL and retry with a fresh code — authorization codes are single-use and short-lived (often ~30s–10min).
- Confirm the pasted code is complete and trimmed (codes are long; partial clipboard copies are common).
- Verify `state` matches the one issued with the authorization URL; if the modal re-mounted, state was lost — restart the flow.
- Check server logs for the provider's token-endpoint error (invalid_client / invalid_grant) and fix the server-side OAuth credentials.
Example fix
// before
const data = await res.json();
if (!res.ok) throw new Error(data.error);
// after
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data?.error || `Code exchange failed (HTTP ${res.status})`); Defensive patterns
Strategy: try-catch
Validate before calling
// verify code and state are both present before POSTing
if (!code?.trim() || !state) {
setError("Both the authorization code and the original state are required");
return;
} Try / catch
try {
const res = await fetch(manualCodeUrl, { method: "POST", body: JSON.stringify({ code: code.trim(), state, ...meta }) });
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.error || `Exchange failed (HTTP ${res.status})`);
} catch (err) {
setError(err.message);
setStep("error");
} Prevention
- Trim the pasted code before sending.
- Restart the flow for a fresh code instead of retrying a failed code.
- Keep the modal mounted between generating the auth URL and submitting the code (state must persist).
- Check server-side client credentials when invalid_client appears.
When it happens
Trigger: The manual-code exchange endpoint for the selected provider (POST with { code, state, ...meta }) returns 4xx/5xx with { error }: invalid/expired/already-used code, state mismatch against the state issued when the auth URL was generated, or a provider-side token failure relayed by the route.
Common situations: Pasting a code from a previous/stale authorization attempt; copying the code partially or with whitespace; server's OAuth client credentials (client secret) misconfigured or rotated; the state cookie/session was lost (different browser session or server restart).
Related errors
- OIDC token exchange failed (${res.status})
- `Token exchange failed: ${error}`
- `Token exchange failed: ${error}`
- `Cline token exchange failed: ${error}`
- `ClinePass token exchange failed: ${error}`
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/065ba9c6dbf22c39.
Report an issue: GitHub.