🤖 Automatic Display
Errors display automatically—no manual error handling code needed in pages.
The Auth layout error handling system provides centralized error alert display across all dashboard pages, eliminating the need for manual error handling code in individual pages.
Error messages automatically display below the navigation bar when users are redirected due to insufficient permissions or other error conditions. The system supports both predefined error codes and custom error messages with three visual alert types.
🤖 Automatic Display
Errors display automatically—no manual error handling code needed in pages.
🎨 Three Alert Types
Error (red), Warning (orange), and Info (blue) styling for different scenarios.
📝 Predefined Codes
Common error codes have user-friendly messages built-in.
⚙️ Custom Messages
Override with custom messages via props for page-specific errors.
No code required! When users are redirected with error parameters, Auth layout automatically displays the error:
---import Auth from '#layouts/Auth.astro'import type { AnyRole } from '#utils/role-types'
// Protect page with role guardconst requiredRoles: AnyRole[] = ['admin']---
<Auth pageTitle="Admin Panel" requiredRoles={requiredRoles}> <!-- Content only visible to admins --> <!-- Unauthorized users redirected with ?error=insufficient_permissions --> <!-- Error displays automatically on destination page --></Auth>What happens:
/dashboard?error=insufficient_permissionsOverride automatic errors with custom messages:
---import Auth from '#layouts/Auth.astro'
const saveError = Astro.url.searchParams.get('save_error')---
<Auth pageTitle="Settings" errorMessage={saveError ? "Failed to save settings. Please try again." : undefined} errorType="error"> <!-- Page content --></Auth>The system includes predefined error messages for common scenarios:
| Error Code | Message |
|---|---|
insufficient_permissions | ”You do not have permission to access that page.” |
session_expired | ”Your session has expired. Please sign in again.” |
invalid_request | ”The request was invalid. Please try again.” |
not_found | ”The requested resource was not found.” |
Usage:
// In API endpoint or middlewareconst redirectUrl = new URL('/dashboard', Astro.url.origin)redirectUrl.searchParams.set('error', 'session_expired')return Astro.redirect(redirectUrl.toString())
// User sees: "Your session has expired. Please sign in again."Control the visual style with errorType prop:
Use for: Critical errors, access denied, authentication failures
<Auth pageTitle="Admin Panel" errorMessage="Access denied. Admin privileges required." errorType="error"> <!-- Content --></Auth>Visual: Red background, red border, dark red text
Use for: Non-critical issues, deprecation notices, temporary limitations
<Auth pageTitle="Settings" errorMessage="Some features are temporarily unavailable." errorType="warning"> <!-- Content --></Auth>Visual: Yellow background, orange border, brown text
Use for: Success messages, informational alerts, status updates
<Auth pageTitle="Profile" errorMessage="Your profile has been updated successfully." errorType="info"> <!-- Content --></Auth>Visual: Blue background, blue border, dark blue text
When multiple error sources are present, the system resolves in this order:
errorMessage prop (highest priority)?error=...)Example:
---// URL: /dashboard?error=insufficient_permissions// But page also has custom error prop---
<Auth pageTitle="Dashboard" errorMessage="Custom error message" errorType="warning"> <!-- Custom message displays, not URL param message --></Auth>---import Auth from '#layouts/Auth.astro'import type { AnyRole } from '#utils/role-types'
const requiredRoles: AnyRole[] = ['team_manager']---
<Auth pageTitle="Team Management" requiredRoles={requiredRoles}> <!-- Unauthorized access automatically redirects with error --></Auth>---import Auth from '#layouts/Auth.astro'
const validationError = Astro.url.searchParams.get('validation_error')const errorMessage = validationError ? "Please check your form inputs and try again." : undefined---
<Auth pageTitle="Create Event" errorMessage={errorMessage} errorType="error"> <form method="POST" action="/api/events/create"> <!-- Form fields --> </form></Auth>---import Auth from '#layouts/Auth.astro'
const successParam = Astro.url.searchParams.get('success')const successMessage = successParam === 'saved' ? 'Your changes have been saved successfully.' : undefined---
<Auth pageTitle="Settings" errorMessage={successMessage} errorType="info"> <!-- Settings content --></Auth>---import Auth from '#layouts/Auth.astro'
const saveError = Astro.url.searchParams.get('save_error')const deleteError = Astro.url.searchParams.get('delete_error')const networkError = Astro.url.searchParams.get('network_error')
const errorMessage = saveError ? "Failed to save. Please check your inputs." : deleteError ? "Cannot delete. Item is referenced by other records." : networkError ? "Network error. Please check your connection." : undefined---
<Auth pageTitle="User Management" errorMessage={errorMessage} errorType="error"> <!-- Content --></Auth>Type: string | undefined
Default: undefined
Description: Custom error message to display in alert
<!-- Automatic (no custom error) --><Auth pageTitle="Dashboard" />
<!-- Custom error --><Auth pageTitle="Settings" errorMessage="Failed to load settings"/>
<!-- Conditional error --><Auth pageTitle="Profile" errorMessage={hasError ? "An error occurred" : undefined}/>Type: 'error' | 'warning' | 'info'
Default: 'error'
Description: Visual style of the alert
<!-- Error (red) --><Auth errorMessage="Access denied" errorType="error" />
<!-- Warning (orange) --><Auth errorMessage="Feature unavailable" errorType="warning" />
<!-- Info (blue) --><Auth errorMessage="Update successful" errorType="info" />API endpoints can set error parameters for display on redirect:
import type { APIRoute } from 'astro'
export const POST: APIRoute = async ({ request, redirect }) => { try { const data = await request.json()
// Validation if (!isValid(data)) { return redirect('/dashboard/users?error=invalid_request') }
// Process update...
// Success return redirect('/dashboard/users?success=user_updated') } catch (error) { // Error return redirect('/dashboard/users?error=database_error') }}To add new predefined error codes, update the ERROR_MESSAGES mapping in src/layouts/Auth.astro:
const ERROR_MESSAGES: Record<string, string> = { // Existing codes insufficient_permissions: 'You do not have permission to access that page.', session_expired: 'Your session has expired. Please sign in again.',
// Add new codes database_error: 'A database error occurred. Please try again later.', rate_limited: 'Too many requests. Please wait before trying again.', maintenance: 'System is under maintenance. Please check back later.',}Then use in redirects:
redirectUrl.searchParams.set('error', 'rate_limited')return Astro.redirect(redirectUrl.toString())Before (Manual approach):
---import Auth from '#layouts/Auth.astro'import CardContainer from '#components/astro/CardContainer.astro'
// Manual error checkingconst errorParam = Astro.url.searchParams.get('error')const errorMessage = errorParam === 'insufficient_permissions' ? 'You do not have permission to access that page.' : null---
<Auth pageTitle="Dashboard"> {/* Manual error display */} {errorMessage && ( <section> <CardContainer> <div class="alert alert-error" role="alert"> <p><strong>Access Denied:</strong> {errorMessage}</p> </div> </CardContainer> </section> )}
<section> <!-- Dashboard content --> </section></Auth>After (Automatic approach):
---import Auth from '#layouts/Auth.astro'---
<Auth pageTitle="Dashboard"> <section> <!-- Dashboard content --> <!-- Error displays automatically above this section --> </section></Auth>Benefits:
<Auth pageTitle="Dashboard"> <!-- Errors display automatically --> <section>Dashboard content</section></Auth><!-- DON'T DO THIS -->---const errorParam = Astro.url.searchParams.get('error')const errorMessage = errorParam === 'insufficient_permissions' ? '...' : null---
<Auth pageTitle="Dashboard"> {errorMessage && <div class="alert">{errorMessage}</div>}</Auth><Auth errorMessage="Failed to save user profile. Please check required fields and try again." errorType="error"><!-- DON'T DO THIS --><Auth errorMessage="Error" errorType="error"><!-- Success message --><Auth errorMessage="Profile updated successfully" errorType="info" />
<!-- Non-critical issue --><Auth errorMessage="Some features temporarily unavailable" errorType="warning" />
<!-- Critical error --><Auth errorMessage="Access denied" errorType="error" /><!-- DON'T DO THIS --><Auth errorMessage="Success!" errorType="error" />The Auth layout provides a secondary-nav named slot that lets pages replace or suppress the default DashboardNavigation section.
When secondary-nav is provided, it completely replaces the default nav section (including the <Nav> wrapper). If the slot is absent, the default DashboardNavigation renders unchanged.
// In Auth.astroconst hasSecondaryNav = Astro.slots.has('secondary-nav')
// Slot replaces default nav when presenthasSecondaryNav ? <slot name="secondary-nav" /> : <Nav><DashboardNavigation /></Nav>Custom navigation with hidden items:
<Auth pageTitle="Dashboard"> <section slot="secondary-nav"> <DashboardNavigation hideItems={['orders']} /> </section>
<!-- page content --></Auth>Entirely custom navigation:
<Auth pageTitle="Settings"> <nav slot="secondary-nav"> <a href="/dashboard">Back to Dashboard</a> <a href="/settings/profile">Profile</a> </nav></Auth>Suppress navigation entirely:
<!-- Empty Fragment suppresses default nav --><Auth pageTitle="Focused Task"> <Fragment slot="secondary-nav" />
<!-- page content with no navigation rendered --></Auth>The dashboard index page (src/pages/dashboard/index.astro) uses this slot to show all navigation items except Orders:
<Auth pageTitle="Dashboard"> <section slot="secondary-nav"> <DashboardNavigation hideItems={['orders']} /> </section>
<!-- dashboard content --></Auth>Problem: Error parameter in URL but no alert shown
Solutions:
errorMessage prop)Problem: Shows raw error code instead of friendly message
Solution: Add error code to ERROR_MESSAGES mapping in Auth.astro
Problem: URL param displays instead of custom prop
Solution: Ensure custom error is not undefined:
❌ <Auth errorMessage={undefined} /> <!-- Falls back to URL param -->✅ <Auth errorMessage="Custom error" /> <!-- Overrides URL param -->src/layouts/Auth.astro
project-docs/02-guides/auth-layout-error-handling.md