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 TypeExample RoutesDefault Response
User-facingGET /:provider, GET /callback/:providerHTML error page or redirect
APIGET /session, POST /signoutJSON: { 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
}
CodeDefault MessageStatus
CSRF_INVALIDInvalid CSRF token403
PKCE_MISSINGMissing PKCE code verifier400
PKCE_CHALLENGE_MISSINGMissing PKCE challenge400
OAUTH_CANCELLEDAuthentication was cancelled200
PROVIDER_NOT_FOUNDProvider not found400
AUTHORIZATION_URL_FAILEDCould not create authorization URL500
CodeDefault MessageStatus
USER_NOT_FOUNDUser not found404
USER_CREATE_FAILEDFailed to create user500
ACCOUNT_ALREADY_LINKEDAccount already linked to another user409
ACCOUNT_LINK_FAILEDFailed to link account500
ACCOUNT_NOT_LINKEDAccount not linked400
CANNOT_UNLINK_LAST_ACCOUNTCannot unlink the last account400
EMAIL_ALREADY_EXISTSAn account with this email already exists409
EMAIL_MISMATCHEmail mismatch between existing account and provider400
LINKING_NOT_ALLOWEDLinking not allowed403
LINK_ONLY_PROVIDERSign-in with this provider is disabled…400
CodeDefault MessageStatus
UNAUTHORIZEDUnauthorized401
FORBIDDENForbidden403
SESSION_INVALIDInvalid session400
SESSION_VALIDATION_FAILEDFailed to validate session500
CodeDefault MessageStatus
TOKEN_INVALIDInvalid token400
TOKEN_EXPIREDToken expired400
CODE_VERIFIER_INVALIDInvalid code verifier400
CodeDefault MessageStatus
NOT_FOUNDNot found404
METHOD_NOT_ALLOWEDMethod not allowed405
INVALID_REQUESTInvalid request400
INVALID_REDIRECT_URLInvalid redirect URL400
UNTRUSTED_HOSTUntrusted redirect host400
UNKNOWN_PROFILEUnknown profile400
CodeDefault MessageStatus
INTERNAL_ERRORAn unexpected error occurred500

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
})