# Waddling Errors Macros - Developer Guide
This guide covers all 7 macros provided by `waddling-errors-macros` for defining error codes with rich metadata.
## Table of Contents
1. [Overview](#overview)
2. [setup! Macro](#1-setup-macro)
3. [component! Macro](#2-component-macro)
4. [primary! Macro](#3-primary-macro)
5. [sequence! Macro](#4-sequence-macro)
6. [diag! Macro](#5-diag-macro)
7. [component_location! Macro](#6-component_location-macro)
8. [#[in_component] Attribute](#7-in_component-attribute)
9. [Complete Example](#complete-example)
10. [Feature Flags](#feature-flags)
11. [Limitations](#limitations)
---
## Overview
The waddling-errors system uses a **4-part error code format**:
```
Severity.Component.Primary.Sequence
│ │ │ │
│ │ │ └── Numeric code (001-999)
│ │ └── Category within component (Token, Login, etc.)
│ └── System component (Auth, Database, etc.)
└── E (Error), W (Warning), I (Info), etc.
```
Example: `E.Auth.Token.EXPIRED` → `E.Auth.Token.031`
### Macro Dependency Order
```
┌─────────┐ ┌─────────────┐ ┌───────────┐ ┌────────────┐
│ setup! │ ──► │ component! │ ──► │ primary! │ ──► │ sequence! │
└─────────┘ └─────────────┘ └───────────┘ └────────────┘
│
▼
┌──────────┐
│ diag! │
└──────────┘
```
- `setup!` must be called first (at crate root)
- `component!`, `primary!`, `sequence!` define building blocks
- `diag!` uses all of them to define error codes
---
## 1. setup! Macro
> ⚠️ **This is the most commonly missed step!** If your `diag!` calls aren't compiling, you probably forgot `setup!` at your crate root.
Configures path resolution for `diag!` to find component, primary, and sequence definitions.
**Key requirements:**
- Must be called **once** per crate
- Must be at **crate root** (lib.rs or main.rs, not inside a module)
- Must be called **before** any `diag!` macro invocations
### Why It's Needed
Without `setup!`, `diag!` expects definitions at fixed paths:
- `crate::components::*`
- `crate::primaries::*`
- `crate::sequences::*`
With `setup!`, you can place definitions anywhere.
### Syntax
```rust
use waddling_errors_macros::setup;
setup! {
components = crate::my_components,
primaries = crate::my_primaries,
sequences = crate::my_sequences,
}
```
### What It Generates
```rust
#[doc(hidden)]
pub mod __wd_paths {
pub use crate::my_components as components;
pub use crate::my_primaries as primaries;
pub use crate::my_sequences as sequences;
}
```
### Partial Configuration
You can specify only some paths - others use defaults:
```rust
setup! {
sequences = crate::errors::sequences,
// components defaults to crate::components
// primaries defaults to crate::primaries
}
```
### Location
**Must be called once at crate root** (lib.rs or main.rs).
### Limitations
- Only one `setup!` per crate
- Must be at crate root level (not inside a module)
- Creates a `__wd_paths` module - don't create your own module with this name
---
## 2. component! Macro
Defines system components (Auth, Database, API, etc.) with optional metadata.
### Basic Syntax
```rust
use waddling_errors_macros::component;
component! {
// Simple: just the name (value = "Auth")
Auth,
// With custom value
Db {
value: "Database",
},
// With full metadata
Api {
value: "API",
docs: "REST API server component",
introduced: "1.0.0",
deprecated: "Use ApiV2 instead",
examples: [
"E.API.Auth.001",
"E.API.Rate.002",
],
tags: ["backend", "http"],
},
}
```
### Supported Fields
| `value` | string | Custom string value (default: identifier as-is) |
| `docs` / `description` | string | Documentation description |
| `introduced` | string | Version when introduced |
| `deprecated` | string | Deprecation notice |
| `examples` | array | Example error codes |
| `tags` | array | Categorization tags |
### Role-Based Documentation
```rust
component! {
Auth {
value: "Auth",
'Pub docs: "Authentication system", // Public docs
'Dev docs: "Handles JWT + session management", // Developer docs
'Int docs: "See auth-service runbook", // Internal docs
},
}
```
### What It Generates
- A constant for each component (e.g., `pub const Auth: &str = "Auth";`)
- Auto-registration with the global registry (with `auto-register` feature)
- Metadata constants for documentation (with `metadata` feature)
### Limitations
- Component names must be valid Rust identifiers
- Cannot use reserved words (e.g., `Error`, `Self`)
- `value` field must be a valid string for use in error codes
- No spaces in `value` - use underscores or PascalCase
---
## 3. primary! Macro
Defines primary categories within components (Token, Login, Session, etc.).
### Basic Syntax
```rust
use waddling_errors_macros::primary;
primary! {
// Simple
Token,
// With metadata
Login {
value: "Login",
docs: "User login and authentication",
related: ["Session", "Token"],
},
// Full metadata
Session {
value: "Session",
docs: "Session management",
introduced: "1.0.0",
examples: [
"E.Auth.Session.EXPIRED",
"E.Auth.Session.INVALID",
],
tags: ["auth", "state"],
related: ["Token", "Login"],
},
}
```
### Supported Fields
| `value` | string | Custom string value (default: identifier as-is) |
| `docs` / `description` | string | Documentation description |
| `introduced` | string | Version when introduced |
| `deprecated` | string | Deprecation notice |
| `examples` | array | Example error codes |
| `tags` | array | Categorization tags |
| `related` | array | Related primary codes |
### Role-Based Documentation
```rust
primary! {
Token {
value: "Token",
'Pub docs: "API token management",
'Dev docs: "JWT tokens with RS256 signing",
'Int docs: "Token rotation every 24h, see secrets-manager",
'Dev examples: ["E.Auth.Token.001"],
},
}
```
### What It Generates
- A constant for each primary (e.g., `pub const Token: &str = "Token";`)
- Auto-registration with the global registry
- Metadata constants for documentation
### Limitations
- Same naming restrictions as components
- Cannot have the same name as a component in the same scope
- Primary names should be unique within their logical grouping
---
## 4. sequence! Macro
Defines sequence numbers (001-999) with semantic meaning and metadata.
### Basic Syntax
```rust
use waddling_errors_macros::sequence;
sequence! {
// Simple
MISSING(1),
// With metadata
INVALID(3) {
description: "Validation failed",
typical_severity: "Error",
hints: [
"Check input format",
"Review validation rules",
],
},
// Full metadata
TIMEOUT(17) {
description: "Operation exceeded time limit",
typical_severity: "Error",
hints: [
"Increase timeout",
"Optimize operation",
],
related: ["18", "19"],
introduced: "1.0.0",
},
}
```
### Supported Fields
| `description` | string | Human-readable description |
| `typical_severity` | string | Typical severity (Error, Warning, Info, etc.) |
| `hints` | array | Helpful hints for resolution |
| `related` | array | Related sequence numbers |
| `introduced` | string | Version when introduced |
### Importing Sequences
You can import sequences from other modules:
```rust
sequence! {
// Import from another module
use crate::common_sequences::{MISSING, INVALID};
// Local sequences
CUSTOM(42) {
description: "Custom application error",
},
}
```
### Standard Sequence Conventions
Following SEQUENCE-CONVENTIONS.md:
| 001-010 | Input/Data validation | MISSING(1), INVALID(3), MISMATCH(2) |
| 011-020 | State/Lifecycle | TIMEOUT(17), STALE(18), IN_PROGRESS(15) |
| 021-030 | Resource/Storage | NOT_FOUND(21), EXHAUSTED(26), CONFLICT(23) |
| 999 | Success/Completion | COMPLETE(999) |
### What It Generates
- A constant for each sequence (e.g., `pub const MISSING: u16 = 1;`)
- Metadata constants (e.g., `pub const MISSING_META: SequenceMetadata`)
- Lookup functions: `lookup_sequence_metadata(u16)` and `lookup_sequence_name(u16)`
- Auto-registration
### Limitations
- Sequence numbers must be 1-999 (0 is reserved)
- Sequence names must be UPPER_SNAKE_CASE
- Duplicate numbers in the same block are compile errors
- Cannot redefine imported sequences
---
## 5. diag! Macro
The main macro for defining complete diagnostic error codes with full metadata.
### Basic Syntax
```rust
use waddling_errors_macros::diag;
diag! {
E.Auth.Token.MISSING: {
message: "JWT token missing from request",
description: "Authorization header not provided",
hints: ["Include Authorization header"],
},
}
```
### Block-Level Options
#### Format Specification
```rust
diag! {
<json, html>, // Generate JSON and HTML documentation
E.Auth.Token.MISSING: { ... },
}
```
Available formats: `json`, `html`, `catalog`, `xml`
#### Namespace
```rust
diag! {
namespace: "my_service", // Override global namespace
E.Auth.Token.MISSING: { ... },
}
```
#### Validation Mode
```rust
// Strict validation (validates against defined components/primaries/sequences)
diag! {
strict(sequence, primary, component),
E.Auth.Token.MISSING: { ... },
}
// Selective validation
diag! {
strict(sequence), // Only validate sequences
E.Auth.Token.MISSING: { ... },
}
// Relaxed mode (default) - no validation
diag! {
relaxed,
E.Auth.Token.MISSING: { ... },
}
```
Validation targets:
- `sequence` - Validates sequence constants exist
- `primary` - Validates primary constants exist
- `component` - Validates component constants exist
- `naming` - Validates naming conventions
- `duplicates` - Validates no duplicate codes
- `sequence_values` - Validates numeric values
- `string_values` - Validates string values
### Visibility Markers
Control where fields appear (compile-time docs vs runtime binary):
| `'C` | Compile-time only | Verbose docs, code snippets |
| `'R` | Runtime only | Tags, related codes in binary |
| `'CR` / `'RC` | Both (default) | Most fields |
```rust
diag! {
E.Auth.Token.MISSING: {
message: "Token missing",
// Compile-time only (verbose, for docs)
'C description: "Detailed explanation for documentation...",
// Runtime only (short, for production)
'R description: "Token required",
// Both contexts (default)
'CR hints: ["Check auth header"],
},
}
```
### Role Markers
Control audience-specific content:
| `'Pub` | Public/Customers | Only public content |
| `'Dev` | Developers | Public + Developer content |
| `'Int` | Internal/SRE | All content |
```rust
diag! {
E.Auth.Token.MISSING: {
message: "Token missing",
// Different hints for different audiences
'CR 'Pub hints: [
"Include your API key",
"Contact support if needed",
],
'CR 'Dev hints: [
"Check JWT signing configuration",
"Verify token middleware order",
],
'CR 'Int hints: [
"Check secrets-manager rotation",
"Review auth-service metrics",
],
'R role: "Public", // Default visibility level
},
}
```
### Hybrid Role Syntax
Combine outer default with inner overrides:
```rust
diag! {
E.Auth.Login.FAILED: {
message: "Login failed",
// Outer role is default, inner markers override
'CR 'Pub hints: [
"Check username and password", // 'Pub (from outer)
'Dev "Verify LDAP connection", // 'Dev (override)
"Contact support", // 'Pub (from outer)
'Int "Check AD sync status", // 'Int (override)
],
},
}
```
### Supported Fields
#### Core Fields (always present)
| `message` | **Yes** | string | Message template with `{{field}}` placeholders |
| `fields` | No | array | Non-PII field names for message interpolation |
| `pii` | No | array | PII field names (use `{{pii/field}}` in message) |
| `severity` | No | ident | Override severity from code |
#### Visibility-Marked Fields
| `description` | `'C`, `'R`, `'CR` | Human-readable description |
| `hints` | `'C`, `'R`, `'CR` | Array of resolution hints |
| `role` | `'R`, `'CR` | Role-based visibility level |
| `tags` | `'R` | Categorization tags (runtime only) |
| `related_codes` | `'R` | Related error codes (runtime only) |
| `code_snippet` | `'C` + role | Rich code examples (compile-time only, multiple allowed) |
| `docs_url` | `'C` | Documentation URL (compile-time only) |
| `introduced` | `'C` | Version introduced (compile-time only) |
| `deprecated` | `'C` | Deprecation notice (compile-time only) |
| `see_also` | `'C` | See also references (compile-time only) |
### Message Templates
```rust
diag! {
E.Auth.Login.FAILED: {
// Regular fields: {{field_name}}
message: "Login failed for user at {{timestamp}}",
fields: [timestamp],
// PII fields: {{pii/field_name}}
// message: "Login failed for {{pii/email}} at {{timestamp}}",
// pii: [email],
},
}
```
### Code Snippets
Code snippets are compile-time only (`'C` marker) and support rich metadata with role-based filtering.
#### Basic Syntax
```rust
diag! {
E.Auth.Token.MISSING: {
message: "Token missing",
// Single code snippet with all fields
'C 'Pub code_snippet: {
language: "javascript",
wrong: "fetch('/api/data')",
correct: "fetch('/api/data', {\n headers: { 'Authorization': 'Bearer <token>' }\n})",
explanation: "Include the Authorization header with your JWT token",
},
},
}
```
#### Available Fields
| `language` | `lang` | No | Syntax highlighting language |
| `wrong` | `incorrect` | No | Example of incorrect code |
| `correct` | `fix` | No | Example of correct code |
| `explanation` | `explain` | No | Brief explanation of the fix |
#### Multiple Snippets with Different Roles
You can define multiple code snippets for different audiences:
```rust
diag! {
E.Auth.Token.MISSING: {
message: "Token missing",
// Public snippet (shown in customer documentation)
'C 'Pub code_snippet: {
language: "javascript",
wrong: "fetch('/api/data')",
correct: "fetch('/api/data', { headers: { 'Authorization': 'Bearer <token>' } })",
explanation: "Include your authentication token",
},
// Developer snippet (shown in developer docs)
'C 'Dev code_snippet: {
language: "rust",
wrong: "client.get(url).send()?",
correct: "client.get(url).header(\"Authorization\", token).send()?",
explanation: "Add Authorization header to request",
},
// Internal snippet (shown only to internal team)
'C 'Int code_snippet: {
language: "bash",
wrong: "curl $URL",
correct: "curl -H \"Authorization: Bearer $(vault read -field=token)\" $URL",
explanation: "Use Vault for token retrieval in scripts",
},
},
}
```
#### Partial Snippets
Not all fields are required - you can provide just `wrong`, just `correct`, or any combination:
```rust
'C 'Pub code_snippet: {
language: "python",
correct: "requests.get(url, headers={'Authorization': f'Bearer {token}'})",
}
```
#### Output Format
In generated JSON, each snippet becomes two entries (if both wrong and correct are present):
```json
{
"code_snippets": [
{
"code": "fetch('/api/data')",
"label": "❌ Incorrect",
"language": "javascript",
"role": "Public"
},
{
"code": "fetch('/api/data', { headers: { 'Authorization': 'Bearer <token>' } })",
"label": "✅ Correct: Include your authentication token",
"language": "javascript",
"role": "Public"
}
]
}
```
### What It Generates
Three constants per diagnostic:
```rust
// Always generated
pub const E_AUTH_TOKEN_MISSING: DiagnosticRuntime = /* ... */;
// With metadata feature
#[cfg(feature = "metadata")]
pub const E_AUTH_TOKEN_MISSING_DOCS: DiagnosticDocs = /* ... */;
#[cfg(feature = "metadata")]
pub const E_AUTH_TOKEN_MISSING_COMPLETE: DiagnosticComplete = /* ... */;
```
Plus auto-registration code (with `auto-register` feature).
### Limitations
- `message` field is required
- Severity must be valid: `E`, `Error`, `W`, `Warning`, `I`, `Info`, `D`, `Debug`, `T`, `Trace`, `B`, `Blocked`, `C`, `Critical`, `S`, `Success`
- Field names in `fields` and `pii` must be valid identifiers
- PII fields and regular fields cannot overlap
- `{{field}}` placeholders must match declared fields
- Code snippets require all sub-fields: `language`, `wrong`, `correct`
- Cannot define the same error code twice in one block
---
## 6. component_location! Macro
Marks a file as containing code for a specific component, enabling **location tracking** in generated documentation. Unlike `#[in_component]`, this macro doesn't require wrapping code in a module.
### Basic Syntax
```rust
use waddling_errors_macros::component_location;
// Simple: mark file as Auth component (default: internal role)
component_location!(Auth);
// With explicit role
component_location!(Auth, role = public);
component_location!(Database, role = developer);
component_location!(Cache, role = internal);
```
### Role-Based Filtering
Roles control **visibility in generated documentation**:
| `public` | Everyone | Public API implementations, examples |
| `developer` | Developers + Internal | Debug utilities, integration code |
| `internal` | Team only (default) | Core implementation, security-sensitive |
**Security by Default**: Unmarked locations default to `internal` to prevent accidental exposure of internal file paths.
### Multiple Components Per File
Unlike `#[in_component]`, you can mark a file as belonging to **multiple components**:
```rust
// src/shared/auth_api.rs
use waddling_errors_macros::component_location;
// This file contains code for both components
component_location!(Auth, role = public);
component_location!(Api, role = developer);
```
### What It Generates
For `component_location!(Auth, role = public)`:
```rust
#[doc(hidden)]
pub mod __component_loc_auth {
pub const COMPONENT: &str = "Auth"; // Preserves case from input
pub const FILE: &str = file!(); // Current file path
pub const ROLE: Option<::waddling_errors::Role> = Some(::waddling_errors::Role::Public);
// Auto-registration (with ctor feature)
#[::ctor::ctor]
fn __register() {
::waddling_errors::registry::register_component_location(COMPONENT, FILE, ROLE);
}
}
```
### Auto-Registration
With the `auto-register` feature enabled, component locations are **automatically registered** at program startup via the `ctor` crate. No manual registration calls needed!
```rust
// Just add the macro - registration happens automatically
component_location!(Auth, role = public);
component_location!(Database, role = internal);
// Later, in doc generation:
let locations = waddling_errors::registry::get_component_locations();
// Returns all registered locations, filtered by role during rendering
```
### Use Cases
1. **Distributed Component Implementations**: Mark files across your codebase
```rust
component_location!(Auth, role = developer);
component_location!(Auth, role = internal);
component_location!(Auth, role = public);
```
2. **Shared Code**: Mark files that serve multiple components
```rust
component_location!(Auth);
component_location!(Crypto);
```
3. **Documentation Organization**: Control what file paths appear in docs per audience
### Limitations
- **One per component per file**: `component_location!(Auth)` twice in the same file causes a compile error (duplicate module definition)
- **Case preserved**: Component name case is preserved (`Auth` → `"Auth"`, not `"AUTH"`)
- **Module name**: Generated module uses lowercase (`__component_loc_auth`)
---
## 7. #[in_component] Attribute
An **attribute macro** that wraps a module to mark it as belonging to a component. Useful when you want the component association tied to a specific module scope.
### Basic Syntax
```rust
use waddling_errors_macros::in_component;
// Default: internal role
#[in_component(Auth)]
mod auth_impl {
// Implementation code
}
// With explicit role
#[in_component(Auth, role = public)]
mod auth_example {
//! Example code for public documentation
}
#[in_component(Database, role = developer)]
mod db_debug {
// Developer debugging utilities
}
```
### What It Generates
For `#[in_component(Auth, role = public)]`:
```rust
mod auth_example {
// Original module contents...
// Added by macro:
#[doc(hidden)]
pub const __COMPONENT: &str = "Auth";
#[doc(hidden)]
pub const __COMPONENT_FILE: &str = file!();
#[doc(hidden)]
pub const __COMPONENT_ROLE: Option<::waddling_errors::Role> =
Some(::waddling_errors::Role::Public);
#[doc(hidden)]
pub fn __register_component_location(registry: &mut ::waddling_errors::doc_generator::DocRegistry) {
registry.register_component_location_with_role(__COMPONENT, __COMPONENT_FILE, __COMPONENT_ROLE);
}
}
```
### Manual Registration
Unlike `component_location!`, `#[in_component]` requires **manual registration**:
```rust
// In doc generation binary
use my_crate::auth_impl;
use my_crate::auth_example;
fn main() {
let mut registry = DocRegistry::new("MyProject", "1.0.0");
// Manually register each module
auth_impl::__register_component_location(&mut registry);
auth_example::__register_component_location(&mut registry);
// Generate docs...
}
```
### Comparison: component_location! vs #[in_component]
| Syntax | Standalone macro | Attribute on module |
| Multiple per file | ✅ Different components | ❌ One per module |
| Auto-registration | ✅ With `ctor` | ❌ Manual calls |
| Module wrapper | ❌ No wrapper needed | ✅ Wraps existing module |
| Use case | Flexible file marking | Module-scoped association |
### When to Use Which
**Use `component_location!`** when:
- You want auto-registration (simpler doc generation)
- A file contains code for multiple components
- You don't want to restructure code into modules
**Use `#[in_component]`** when:
- You want component association tied to a specific module
- You need the module structure for other reasons
- You prefer explicit registration control
---
## 9. Complete Example
```rust
// src/lib.rs or src/main.rs
use waddling_errors_macros::{component, diag, primary, sequence, setup};
// Step 1: Configure paths
setup! {
components = crate::components,
primaries = crate::primaries,
sequences = crate::sequences,
}
// Step 2: Define components
pub mod components {
use waddling_errors_macros::component;
component! {
Auth {
value: "Auth",
docs: "Authentication and authorization",
tags: ["security"],
},
Api {
value: "API",
docs: "REST API endpoints",
},
}
}
// Step 3: Define primaries
pub mod primaries {
use waddling_errors_macros::primary;
primary! {
Token {
value: "Token",
docs: "JWT token handling",
related: ["Session"],
},
Session {
value: "Session",
docs: "User session management",
},
}
}
// Step 4: Define sequences
pub mod sequences {
use waddling_errors_macros::sequence;
sequence! {
MISSING(1) {
description: "Required item not found",
typical_severity: "Error",
hints: ["Check if item exists"],
},
INVALID(3) {
description: "Validation failed",
typical_severity: "Error",
},
EXPIRED(31) {
description: "Item has expired",
typical_severity: "Error",
},
}
}
// Step 5: Define error codes
pub mod errors {
use waddling_errors_macros::diag;
diag! {
<json, html>, // Generate documentation
E.Auth.Token.MISSING: {
message: "JWT token missing from Authorization header",
'CR 'Pub description: "No authentication token was provided",
'CR 'Dev description: "Check middleware order - auth should run first",
'CR 'Pub hints: [
"Include Authorization: Bearer <token> header",
"Obtain a token from /auth/login endpoint",
],
'CR 'Dev hints: [
"Verify auth middleware is registered",
"Check CORS preflight handling",
],
'R role: "Public",
'R tags: ["auth", "security"],
'C introduced: "1.0.0",
'C docs_url: "https://docs.example.com/auth",
},
E.Auth.Token.EXPIRED: {
message: "JWT token expired at {{expiry}}",
fields: [expiry],
'CR description: "The token's exp claim is in the past",
'CR hints: [
"Use refresh token to obtain new access token",
"Re-authenticate if refresh token also expired",
],
'R role: "Public",
'R tags: ["auth", "session"],
'R related_codes: ["E.Auth.Token.MISSING", "E.Auth.Token.INVALID"],
},
}
}
```
---
## 10. Feature Flags
| `metadata` | Enables `*_DOCS` and `*_COMPLETE` constants |
| `auto-register` | Enables automatic registration with global registry |
| `doc-gen` | Enables documentation generation |
| `hash` | Enables hash-based error codes (e.g., `Hx7Kp`) |
### Recommended Combinations
```toml
# Development (full features)
[dependencies]
waddling-errors-macros = { version = "0.7", features = ["metadata", "auto-register", "doc-gen", "hash"] }
# Production (minimal)
[dependencies]
waddling-errors-macros = { version = "0.7", features = ["hash"] }
```
---
## 11. Limitations
### General Limitations
1. **Crate Root Requirement**: `setup!` must be at crate root
2. **Single Namespace**: One `setup!` per crate
3. **No Dynamic Content**: All values must be compile-time constants
4. **Identifier Restrictions**: Component/Primary/Sequence names must be valid Rust identifiers
### Validation Limitations
1. **Cross-Crate Validation**: Cannot validate against definitions in other crates
2. **Runtime Validation**: No runtime validation of error codes
3. **Duplicate Detection**: Only detects duplicates within same `diag!` block
### Documentation Limitations
1. **Format Support**: Only JSON, HTML, XML, and catalog formats
2. **Custom Renderers**: Require implementing traits manually
3. **Live Reload**: Documentation regenerated on rebuild only
### Performance Considerations
1. **Compile Time**: Large numbers of diagnostics increase compile time
2. **Binary Size**: `metadata` feature increases binary size
3. **Registration**: `auto-register` adds startup overhead
### Workarounds
**For cross-crate validation:**
```rust
// In shared crate, export sequences
pub use my_lib::sequences::*;
// In consumer crate
setup! {
sequences = my_lib::sequences,
}
```
**For dynamic content:**
```rust
// Use fields instead of hardcoding
message: "Error at {{location}}",
fields: [location],
// Then provide location at runtime
```
---
## Additional Resources
- [SEQUENCE-CONVENTIONS.md](../../SEQUENCE_CONVENTIONS.md) - Standard sequence number meanings
- [examples/](../examples/) - Working examples
- [waddling-errors core](../../waddling-errors/) - Core library documentation