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

  1. Regenerate the auth URL and retry with a fresh code — authorization codes are single-use and short-lived (often ~30s–10min).
  2. Confirm the pasted code is complete and trimmed (codes are long; partial clipboard copies are common).
  3. Verify `state` matches the one issued with the authorization URL; if the modal re-mounted, state was lost — restart the flow.
  4. 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

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


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/065ba9c6dbf22c39. Report an issue: GitHub.