Mintplex-Labs/anything-llm · error · Error

text

Error message

text

What it means

Thrown by simpleSSOLogin in the AnythingLLM frontend when GET /api/request-token/sso/simple?token=... responds non-2xx and the body is NOT JSON (does not start with '{'). The raw body text becomes the Error message — so when a reverse proxy returns an HTML 502/504 page, the 'message' is an entire HTML document. If the body is JSON it is parsed and returned as a structured { valid:false, message } payload instead, never throwing.

Source

Thrown at frontend/src/models/system.js:818

      this.cacheKeys.canViewChatHistory,
      JSON.stringify({ viewable: isViewable, lastFetched: Date.now() })
    );
    return { viewable: isViewable, error: null };
  },

  /**
   * Validates a temporary auth token and logs in the user if the token is valid.
   * @param {string} publicToken - the token to validate against
   * @returns {Promise<{valid: boolean, user: import("@prisma/client").users | null, token: string | null, message: string | null}>}
   */
  simpleSSOLogin: async function (publicToken) {
    return fetch(`${API_BASE}/request-token/sso/simple?token=${publicToken}`, {
      method: "GET",
    })
      .then(async (res) => {
        if (!res.ok) {
          const text = await res.text();
          if (!text.startsWith("{")) throw new Error(text);
          return JSON.parse(text);
        }
        return await res.json();
      })
      .catch((e) => {
        console.error(e);
        return { valid: false, user: null, token: null, message: e.message };
      });
  },

  /**
   * Fetches the app version from the server.
   * @returns {Promise<string | null>} The app version.
   */
  fetchAppVersion: async function () {
    const cache = window.localStorage.getItem(this.cacheKeys.deploymentVersion);
    const { version, lastFetched } = cache
      ? safeJsonParse(cache, { version: null, lastFetched: 0 })

View on GitHub (pinned to 20f6d3546c)

Solutions

  1. Generate and use a fresh SSO link — tokens are single-use and short-lived.
  2. Look at the message shape: HTML means infrastructure (proxy/app down), short plain text usually means the app rejected the token.
  3. Confirm simple SSO is enabled on the server instance.
  4. Copy the complete token with no truncation or surrounding whitespace.

Example fix

// before
if (!text.startsWith('{')) throw new Error(text); // whole HTML page becomes the message

// after — keep only a clamped first line
if (!text.startsWith('{')) {
  const firstLine = text.trim().split('\n')[0].slice(0, 200);
  throw new Error(firstLine || `SSO login failed (${res.status})`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// run before System.simpleSSOLogin
if (!publicToken || /\s/.test(publicToken)) {
  throw new Error('SSO token missing or contains whitespace');
}

Type guard

/** @param {string} text @returns {text is string} */
function isHtmlErrorBody(text) {
  const t = text.trimStart().toLowerCase();
  return t.startsWith('<!doctype') || t.startsWith('<html');
}

Try / catch

const { valid, user, token, message } = await System.simpleSSOLogin(publicToken);
if (!valid && isHtmlErrorBody(message || '')) {
  showInfraError('Login gateway unavailable — try again later'); // proxy HTML page
} else if (!valid) {
  showLoginError(message || 'SSO token rejected');
}

Prevention

When it happens

Trigger: Authenticating with an invalid, expired, or already-consumed single-use SSO token (server replies plain-text error); simple SSO not enabled server-side; the app is down behind nginx/traefik and the proxy's HTML error page is returned; token truncated or padded with whitespace when copy-pasted.

Common situations: SSO links older than their TTL; email/chat clients breaking the URL across lines; app container restarting while the user clicks the link; gateway misrouting /api paths.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@20f6d3546c (2026-08-18). Data as JSON: /api/errors/e4d83b8465dee1e2. Report an issue: GitHub.