{"record":{"id":"ddaed323157a523c","repo":"decolua/9router","slug":"data-error-authentication-failed","errorCode":null,"errorMessage":"data.error || \"Authentication failed\"","messagePattern":"data\\.error \\|\\| \"Authentication failed\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/shared/components/GitLabAuthModal.js","lineNumber":73,"sourceCode":"    setOauthMeta({ baseUrl: baseUrl.trim() || GITLAB_COM, clientId: clientId.trim(), clientSecret: clientSecret.trim() });\n    setShowOAuth(true);\n  };\n\n  const handlePATSubmit = async () => {\n    if (!pat.trim()) {\n      setError(\"Personal Access Token is required\");\n      return;\n    }\n    setLoading(true);\n    setError(null);\n    try {\n      const res = await fetch(\"/api/oauth/gitlab/pat\", {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application/json\" },\n        body: JSON.stringify({ token: pat.trim(), baseUrl: baseUrl.trim() || GITLAB_COM }),\n      });\n      const data = await res.json();\n      if (!res.ok) throw new Error(data.error || \"Authentication failed\");\n      onSuccess?.();\n      handleClose();\n    } catch (err) {\n      setError(err.message);\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  if (!isOpen) return null;\n\n  // Sub-modal for OAuth PKCE flow\n  if (showOAuth && oauthMeta) {\n    return (\n      <OAuthModal\n        isOpen\n        provider=\"gitlab\"\n        providerInfo={providerInfo}","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/shared/components/GitLabAuthModal.js#L55-L91","documentation":"GitLabAuthModal's handlePATSubmit POSTs a personal access token (plus optional self-hosted baseUrl) to /api/oauth/gitlab/pat and throws Error(data.error || 'Authentication failed') on any non-ok response. The thrown message populates the modal's error display. The generic fallback appears when the backend responds with an error status but no `error` field.","triggerScenarios":"Submitting a PAT that GitLab rejects (expired, revoked, wrong scopes like missing `api`/`read_user`), a bad baseUrl for self-hosted GitLab, a 401 from the backend validating the token, or any non-2xx where the JSON lacks an error field.","commonSituations":"PAT created with insufficient scopes; token expired on self-hosted GitLab with strict expiry policy; baseUrl typo (e.g. missing https:// or trailing path) making validation fail; GitLab instance unreachable from the server.","solutions":["Create a fresh PAT with the required scopes (api, read_user) and paste it without whitespace","Verify baseUrl is the correct GitLab root URL (https://gitlab.example.com), falling back to GITLAB_COM if self-hosted is not intended","Test the PAT directly: curl -H \"PRIVATE-TOKEN: <pat>\" <baseUrl>/api/v4/user","Check the /api/oauth/gitlab/pat route logs for the upstream failure reason if the fallback message hides it"],"exampleFix":"// before\nif (!res.ok) throw new Error(data.error || \"Authentication failed\");\n// after\nif (!res.ok) throw new Error(data.error || `GitLab authentication failed (HTTP ${res.status})`);","handlingStrategy":"validation","validationCode":"const token = pat.trim();\nif (!token) { setError(\"Personal access token is required\"); return; }\nif (baseUrl && !/^https?:\\/\\//.test(baseUrl)) { setError(\"baseUrl must start with http(s)://\"); return; }","typeGuard":"function isValidPatInput(v) { return typeof v === \"string\" && v.trim().length >= 20; }","tryCatchPattern":"try {\n  const res = await fetch(\"/api/oauth/gitlab/pat\", { method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body: JSON.stringify({ token, baseUrl }) });\n  const data = await res.json();\n  if (!res.ok) throw new Error(data.error || `Authentication failed (HTTP ${res.status})`);\n  onSuccess?.();\n} catch (err) {\n  setError(err.message);\n}","preventionTips":["Create PATs with the required scopes (api, read_user) and a sane expiry","Validate PATs with a direct GitLab API call before wiring them in","Double-check self-hosted baseUrl formatting (root URL, https, no trailing /api)","Regenerate tokens proactively before expiry on strict GitLab instances"],"tags":["auth","gitlab","pat","oauth"],"backgroundTag":"pat-authentication-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}