{"record":{"id":"065ba9c6dbf22c39","repo":"decolua/9router","slug":"data-error-065ba9","errorCode":null,"errorMessage":"data.error","messagePattern":"data\\.error","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/shared/components/OAuthModal.js","lineNumber":89,"sourceCode":"\n  // Exchange tokens\n  const exchangeTokens = useCallback(async (code, state) => {\n    if (!authData) return;\n    try {\n      const res = await fetch(`/api/oauth/${provider}/exchange`, {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application/json\" },\n        body: JSON.stringify({\n          code,\n          redirectUri: authData.redirectUri,\n          codeVerifier: authData.codeVerifier,\n          state,\n          ...(oauthMeta ? { meta: oauthMeta } : {}),\n        }),\n      });\n\n      const data = await res.json();\n      if (!res.ok) throw new Error(data.error);\n\n      setStep(\"success\");\n      onSuccess?.();\n    } catch (err) {\n      setError(err.message);\n      setStep(\"error\");\n    }\n  }, [authData, provider, onSuccess, oauthMeta]);\n\n  const completeXaiManualCode = useCallback(async (code) => {\n    if (!authData?.state) return;\n    try {\n      const res = await fetch(\"/api/oauth/xai/manual-code\", {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application/json\" },\n        body: JSON.stringify({ code, state: authData.state }),\n      });\n      const data = await res.json();","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/shared/components/OAuthModal.js#L71-L107","documentation":"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`.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nconst data = await res.json();\nif (!res.ok) throw new Error(data.error);\n// after\nconst data = await res.json().catch(() => ({}));\nif (!res.ok) throw new Error(data?.error || `Code exchange failed (HTTP ${res.status})`);","handlingStrategy":"try-catch","validationCode":"// verify code and state are both present before POSTing\nif (!code?.trim() || !state) {\n  setError(\"Both the authorization code and the original state are required\");\n  return;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const res = await fetch(manualCodeUrl, { method: \"POST\", body: JSON.stringify({ code: code.trim(), state, ...meta }) });\n  const data = await res.json().catch(() => ({}));\n  if (!res.ok) throw new Error(data.error || `Exchange failed (HTTP ${res.status})`);\n} catch (err) {\n  setError(err.message);\n  setStep(\"error\");\n}","preventionTips":["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."],"tags":["oauth","token-exchange","manual-code"],"backgroundTag":"oauth-code-exchange-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}