Why configure exceptions in core
Central exception configuration keeps teams from handling the same failure category in different ways across modules. By defining policy in spraxium.config.ts, runtime behavior stays coherent even when multiple features and teams evolve in parallel, because every exception follows the same resolution rules regardless of where it originates. This also reduces refactor risk significantly, since changing the fallback strategy or updating a layout mapping requires touching one policy location instead of hunting through dozens of command and listener files scattered across the project.
This page is about policy, not implementation. The goal is to decide how the application should respond when an exception escapes a handler, which layout should render it, and how much should be logged for operators.
Minimal configuration
A small baseline already gives meaningful control over how your bot handles errors in production. Defining a default layout fallback and an unhandled logging policy ensures that every exception in the system produces a user-facing response and an operational signal, even when you have not mapped that specific exception class to a dedicated layout yet. This prevents empty or broken responses that confuse users during unexpected failure scenarios.
import { defineConfig } from '@spraxium/core';
export default defineConfig({
exceptions: {
logUnhandled: true,
layouts: {
default: DefaultFriendlyLayout,
},
},
});Where layouts fit
Layouts belong to error presentation policy, and this page focuses on central configuration, fallback behavior, and governance through config maps rather than layout implementation details. For the full layout strategy covering what layouts are, how to create them, the precedence between decorators and config mappings, and when to use per-instance overrides, see Layout Strategy.
Configuration with class mapping
When your application has multiple domains like moderation, billing, and onboarding, mapping layouts by exception class preserves context and tone for each type of failure. Each failure family can return language and guidance appropriate to the user's action, which significantly improves the error experience compared to a single generic message for every scenario in the bot.
import { defineConfig } from '@spraxium/core';
import {
DefaultFriendlyLayout,
CooldownLayout,
PermissionDeniedLayout,
ArgumentLayout,
} from './layouts/exceptions.layouts';
export default defineConfig({
exceptions: {
logUnhandled: true,
layouts: {
default: DefaultFriendlyLayout,
CooldownException: CooldownLayout,
PermissionDeniedException: PermissionDeniedLayout,
ArgumentException: ArgumentLayout,
},
},
});Configuration keys reference
| Key | Type | Usage |
|---|---|---|
| exceptions.layouts | ExceptionLayoutMap | Map exception class names to layout classes |
| exceptions.layouts.default | ExceptionLayoutClass | Global fallback when no specific mapping exists |
| exceptions.logUnhandled | boolean | Controls logging for unknown thrown errors |
When to map a dedicated layout
Add a dedicated layout mapping when a failure category needs its own tone, guidance, or structure. Permission denials, cooldowns, invalid arguments, and business-rule exceptions are the common examples because the response should tell the user what happened and what to do next.
Do not create a custom layout for every exception class by default. A good default layout plus a small number of deliberate mappings usually stays easier to maintain than a huge map of narrowly different responses.
Layout resolution order
Layout selection follows a stable order that determines which layout renders the response when multiple customization options coexist. Understanding this order prevents ambiguity when local overrides and global fallback configurations are active at the same time, and it allows you to layer policies predictably from most specific to most general.
- Per-instance override through the exception object directly
- Entry in the config layout map matching the exception class name
- Class decorator-level layout set with
@WithLayout() - The default layout defined in configuration
- The framework built-in default layout as the final fallback
Class names are keys
The layout map uses exception class names as keys. If a class is renamed during refactoring, update the mapping to avoid accidental fallback behavior that silently changes the user experience.
Environment strategy
A common team baseline is to keep the unhandled logging option set to true in staging and production while preserving friendly layouts in all environments. This keeps diagnostics strong for operations without leaking internal details to users, because layout communicates with users while logging communicates with the team responsible for monitoring and incident response.
One practical policy split looks like this:
- Local development: friendly layouts, unhandled logging on, fast iteration.
- Staging: friendly layouts, unhandled logging on, parity with production behavior.
- Production: friendly layouts, unhandled logging on, class mappings only for important user-facing failure families.