Skip to content

Error Handling

This guide explains how to handle gau errors.

All errors thrown by gau handlers are GauError instances with a specific error code. You can import and use these in your own code:

import { GauError, ErrorCodes } from '@rttnd/gau'
// Using default message and status
throw new GauError(ErrorCodes.UNAUTHORIZED)
// With options only
throw new GauError(ErrorCodes.CSRF_INVALID, { redirectUrl: '/login' })
// With custom message
throw new GauError(ErrorCodes.PROVIDER_NOT_FOUND, `Provider "${id}" not found`)

The onError hook lets you customize how errors are handled. You can log errors, modify responses, or redirect users to specific pages based on the error type.

import { createAuth, ErrorCodes, redirect } from '@rttnd/gau'
const auth = createAuth({
// ... other options
onError: ({ error, request }) => {
// Log all auth errors
console.error('Auth error:', error.code, error.message)
// Return a Response to override default behavior
if (error.code === ErrorCodes.CSRF_INVALID) {
return redirect('/login?error=session_expired')
}
// Return undefined to fall back to errorRedirect or default behavior
},
})

For user-facing errors (OAuth flow), you can specify a URL to redirect to. Error details are passed as query parameters.

const auth = createAuth({
// Redirect user-facing errors to your custom error page
errorRedirect: '/auth/error',
})

Gau uses route-based detection to determine whether to return HTML or JSON:

Request Type Example Routes Default Response
User-facing GET /:provider, GET /callback/:provider HTML error page or redirect
API GET /session, POST /signout JSON: { error: 'message', code: 'ERROR_CODE' }
  1. If onError returns a Response, that’s used
  2. For user-facing requests with errorRedirect set, redirects with query params
  3. Otherwise, renders a styled HTML error page (user-facing) or returns JSON (API)

When using errorRedirect, gau passes these query parameters:

  • code - The error code (e.g., CSRF_INVALID)
  • message - Human-readable error message
  • status - HTTP status code
  • redirect - Optional URL to redirect back to
src/routes/auth/error/+page.svelte
<script lang="ts">
import { page } from '$app/state'
import { ErrorCodes, type ErrorCode } from '@rttnd/gau'
const code = $derived(page.url.searchParams.get('code') as ErrorCode | null)
const message = $derived(page.url.searchParams.get('message'))
const redirect = $derived(page.url.searchParams.get('redirect') || '/')
</script>
<svelte:head>
<title>Authentication Error</title>
</svelte:head>
<div class="error-container">
<h1>Authentication Error</h1>
<p class="message">{message}</p>
{#if code}
<p class="code">{code}</p>
{/if}
<a href={redirect}>Go back</a>
</div>

When making API calls, errors are returned as JSON:

const response = await fetch('/api/auth/session')
const data = await response.json()
if ('error' in data) {
console.error('Error:', data.code, data.error)
// data.code is the error code (e.g., 'UNAUTHORIZED')
// data.error is the human-readable message
}
import { ErrorCodes } from '@rttnd/gau'
if (data.code === ErrorCodes.UNAUTHORIZED) {
// Redirect to login
}
if (data.code === ErrorCodes.ACCOUNT_ALREADY_LINKED) {
// Show "account already linked" message
}
Code Default Message Status
CSRF_INVALID Invalid CSRF token 403
PKCE_MISSING Missing PKCE code verifier 400
PKCE_CHALLENGE_MISSING Missing PKCE challenge 400
OAUTH_CANCELLED Authentication was cancelled 200
PROVIDER_NOT_FOUND Provider not found 400
AUTHORIZATION_URL_FAILED Could not create authorization URL 500
Code Default Message Status
USER_NOT_FOUND User not found 404
USER_CREATE_FAILED Failed to create user 500
ACCOUNT_ALREADY_LINKED Account already linked to another user 409
ACCOUNT_LINK_FAILED Failed to link account 500
ACCOUNT_NOT_LINKED Account not linked 400
CANNOT_UNLINK_LAST_ACCOUNT Cannot unlink the last account 400
EMAIL_ALREADY_EXISTS An account with this email already exists 409
EMAIL_MISMATCH Email mismatch between existing account and provider 400
LINKING_NOT_ALLOWED Linking not allowed 403
LINK_ONLY_PROVIDER Sign-in with this provider is disabled… 400
Code Default Message Status
UNAUTHORIZED Unauthorized 401
FORBIDDEN Forbidden 403
SESSION_INVALID Invalid session 400
SESSION_VALIDATION_FAILED Failed to validate session 500
Code Default Message Status
TOKEN_INVALID Invalid token 400
TOKEN_EXPIRED Token expired 400
CODE_VERIFIER_INVALID Invalid code verifier 400
Code Default Message Status
NOT_FOUND Not found 404
METHOD_NOT_ALLOWED Method not allowed 405
INVALID_REQUEST Invalid request 400
INVALID_REDIRECT_URL Invalid redirect URL 400
UNTRUSTED_HOST Untrusted redirect host 400
UNKNOWN_PROFILE Unknown profile 400
Code Default Message Status
INTERNAL_ERROR An unexpected error occurred 500

If you prefer gau’s built-in styled error pages instead of redirecting, set errorRedirect to undefined:

const auth = createAuth({
errorRedirect: undefined, // Use gau's built-in error pages
})