moeru-ai/airi · error · Error

Auth request failed (${response.status})

Error message

Auth request failed (${response.status})

What it means

Thrown by postAuthJSON when a POST to /api/auth<path> returns a non-2xx status and the response body has no extractable Better Auth message field. The fallback string includes the HTTP status code. extractAuthError is tried first for data.message / data.error / data.error.message; only when none match does this generic message surface.

Source

Thrown at apps/ui-server-auth/src/modules/auth-fetch.ts:70

  const endpoint = new URL(`/api/auth${path}`, base.apiServerUrl)

  const response = await fetchImpl(endpoint.toString(), {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
    credentials: 'include',
  })

  let data: unknown
  try {
    data = await response.json()
  }
  catch {
    data = null
  }

  if (!response.ok) {
    throw new Error(extractAuthError(data) ?? `Auth request failed (${response.status})`)
  }

  return parse(data, response)
}

/**
 * GET `/api/auth<path>` and parse the response with `parse`.
 *
 * Use when:
 * - Reading a Better Auth GET endpoint (e.g. `/get-session`) from the UI and
 *   you want the same `credentials: include` + error-shape handling as
 *   {@link postAuthJSON}.
 *
 * Expects:
 * - `path` starts with a leading slash.
 * - `parse` runs only on 2xx responses; non-2xx throws with the server message.
 *
 * Returns:

View on GitHub (pinned to 27111382b4)

Solutions

  1. Check response.status in the error context — 401 means bad credentials, 5xx means server/proxy fault.
  2. Verify apiServerUrl points at the running auth service (dev: http://localhost:3000).
  3. If the body is non-JSON, inspect the proxy/server logs; the generic message means extractAuthError found nothing.
  4. Extend extractAuthError if the server returns a new error shape, so users see the real reason.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await postAuthJSON(base, '/sign-in/email', body, parse)
} catch (e) {
  const statusMatch = e.message.match(/\((\d+)\)$/)
  const status = statusMatch ? Number(statusMatch[1]) : null
  if (status === 401) // invalid credentials
  else if (status && status >= 500) // server/proxy fault, retry or surface
}

Prevention

When it happens

Trigger: POST /api/auth/sign-in/email with wrong credentials where the server returns a non-standard error shape; the auth server is unreachable and returns an HTML 502 with no JSON (data stays null); CORS/network error producing a 500 with empty body.

Common situations: Auth server (server/apps/auth) is down or misconfigured; a reverse proxy (Caddy) returns an error page instead of JSON; the API server URL is wrong (apiServerUrl points to the wrong origin); version mismatch between client and server changing the error envelope shape.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/44eac42475ba5a65. Report an issue: GitHub.