decolua/9router · error · Error

data.error || "Authentication failed"

Error message

data.error || "Authentication failed"

What it means

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.

Source

Thrown at src/shared/components/GitLabAuthModal.js:73

    setOauthMeta({ baseUrl: baseUrl.trim() || GITLAB_COM, clientId: clientId.trim(), clientSecret: clientSecret.trim() });
    setShowOAuth(true);
  };

  const handlePATSubmit = async () => {
    if (!pat.trim()) {
      setError("Personal Access Token is required");
      return;
    }
    setLoading(true);
    setError(null);
    try {
      const res = await fetch("/api/oauth/gitlab/pat", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ token: pat.trim(), baseUrl: baseUrl.trim() || GITLAB_COM }),
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || "Authentication failed");
      onSuccess?.();
      handleClose();
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  if (!isOpen) return null;

  // Sub-modal for OAuth PKCE flow
  if (showOAuth && oauthMeta) {
    return (
      <OAuthModal
        isOpen
        provider="gitlab"
        providerInfo={providerInfo}

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Create a fresh PAT with the required scopes (api, read_user) and paste it without whitespace
  2. Verify baseUrl is the correct GitLab root URL (https://gitlab.example.com), falling back to GITLAB_COM if self-hosted is not intended
  3. Test the PAT directly: curl -H "PRIVATE-TOKEN: <pat>" <baseUrl>/api/v4/user
  4. Check the /api/oauth/gitlab/pat route logs for the upstream failure reason if the fallback message hides it

Example fix

// before
if (!res.ok) throw new Error(data.error || "Authentication failed");
// after
if (!res.ok) throw new Error(data.error || `GitLab authentication failed (HTTP ${res.status})`);
Defensive patterns

Strategy: validation

Validate before calling

const token = pat.trim();
if (!token) { setError("Personal access token is required"); return; }
if (baseUrl && !/^https?:\/\//.test(baseUrl)) { setError("baseUrl must start with http(s)://"); return; }

Type guard

function isValidPatInput(v) { return typeof v === "string" && v.trim().length >= 20; }

Try / catch

try {
  const res = await fetch("/api/oauth/gitlab/pat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token, baseUrl }) });
  const data = await res.json();
  if (!res.ok) throw new Error(data.error || `Authentication failed (HTTP ${res.status})`);
  onSuccess?.();
} catch (err) {
  setError(err.message);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


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