{"record":{"id":"d1b5cf60528ac685","repo":"decolua/9router","slug":"data-error-import-failed","errorCode":null,"errorMessage":"data.error || \"Import failed\"","messagePattern":"data\\.error \\|\\| \"Import failed\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/shared/components/CursorAuthModal.js","lineNumber":79,"sourceCode":"    }\n\n    setImporting(true);\n    setError(null);\n\n    try {\n      const res = await fetch(\"/api/oauth/cursor/import\", {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application/json\" },\n        body: JSON.stringify({\n          accessToken: accessToken.trim(),\n          machineId: machineId.trim(),\n        }),\n      });\n\n      const data = await res.json();\n\n      if (!res.ok) {\n        throw new Error(data.error || \"Import failed\");\n      }\n\n      onSuccess?.();\n      onClose();\n    } catch (err) {\n      setError(err.message);\n    } finally {\n      setImporting(false);\n    }\n  };\n\n  return (\n    <Modal isOpen={isOpen} title=\"Connect Cursor IDE\" onClose={onClose}>\n      <div className=\"flex flex-col gap-4\">\n        {/* Auto-detecting state */}\n        {autoDetecting && (\n          <div className=\"text-center py-6\">\n            <div className=\"size-16 mx-auto mb-4 rounded-full bg-primary/10 flex items-center justify-center\">","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/shared/components/CursorAuthModal.js#L61-L97","documentation":"CursorAuthModal's handleImportToken POSTs an imported Cursor token to the backend import endpoint and, when the HTTP response is not ok, throws Error(data.error || 'Import failed') so the server-provided reason (or the generic fallback) surfaces in the modal's error state. The message string shown is the fallback when the API returned no error field.","triggerScenarios":"Submitting the import form where the endpoint responds non-200 (invalid/expired token, malformed body, upstream Cursor auth rejected the token) and the JSON body lacks an `error` field.","commonSituations":"Pasting an expired or wrong-account Cursor access token; token copied incompletely (truncated/whitespace); backend cannot validate against Cursor's API (network/upstream outage); API version drift removing the error field.","solutions":["Re-copy the Cursor token completely from a valid, logged-in session and retry","Check the server response/health endpoint — if upstream Cursor is unreachable the import legitimately fails","Inspect the API route's response shape; if it returns errors without an `error` field, fix the handler to include one","Verify the endpoint path the modal posts to still exists (404 would also land here)"],"exampleFix":"// before\nthrow new Error(data.error || \"Import failed\");\n// after\nthrow new Error(data.error || `Import failed (HTTP ${res.status})`);","handlingStrategy":"try-catch","validationCode":"const token = tokenInput.trim();\nif (!token) { setError(\"Paste a Cursor token first\"); return; }","typeGuard":"function isNonEmptyString(v) { return typeof v === \"string\" && v.trim().length > 0; }","tryCatchPattern":"try {\n  const res = await fetch(\"/api/.../import\", { method: \"POST\", body: JSON.stringify({ token }) });\n  const data = await res.json();\n  if (!res.ok) throw new Error(data.error || `Import failed (HTTP ${res.status})`);\n  onSuccess?.();\n} catch (err) {\n  setError(err.message);\n}","preventionTips":["Trim and sanity-check token format before submitting","Re-copy tokens from a fresh session — they expire often","Surface the server's `error` field verbatim to aid debugging","Include res.status in fallback messages to disambiguate 401 vs 500"],"tags":["auth","cursor","token","import"],"backgroundTag":"token-import-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}