WDP Part 4: Sequence Field Specification
Specification of the Sequence Field (Part 4) of the Waddling Diagnostic Protocol (WDP) error code structure. The sequence field uniquely identifies a specific diagnostic code within the Component.Primary namespace.
Abstract#
This document specifies the Sequence Field (Part 4) of the Waddling Diagnostic Protocol (WDP) error code structure. The sequence field uniquely identifies a specific diagnostic code within the Component.Primary namespace, providing the most granular level of classification.
Specification Navigation: See STRUCTURE.md for an overview of all WDP specification parts.
Conformance
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
1. Introduction#
The sequence field is the fourth part of the five-part WDP error code structure:
Complete WDP Structure:
Severity.Component.Primary.Sequence -> CompactID
^ ^ ^ ^ ^
Part 1 Part 2 Part 3 Part 4 Part 5This Document Focus:
Severity.Component.Primary.Sequence -> CompactID
^^^^^^^^
Part 4: Unique identifier within category (THIS DOCUMENT)The sequence field provides the final level of specificity, uniquely identifying a particular diagnostic event within the Component.Primary namespace.
2. Purpose#
The sequence field serves to:
- Uniquely identify specific diagnostic codes within a category
- Enable evolution - Add new error codes over time without breaking existing ones
- Provide ordering for related diagnostics
- Support conventions for common error patterns (see 7-SEQUENCE-CONVENTIONS.md)
- Form canonical identity for hash computation
Example
Component.Primary = Auth.Token
Sequences within Auth.Token:
001 - Token missing
002 - Token malformed
003 - Token invalid signature
004 - Token expired
005 - Token not yet valid
...Catalog Evolution Example:
Version 1.0: E.Auth.Token.001, E.Auth.Token.003, E.Auth.Token.018
Version 1.1: Add E.Auth.Token.031 (new error type)
Version 1.2: Add E.Auth.Token.032 (another new error type)
→ Existing codes (001-003) remain unchanged
→ New codes added incrementally
→ No breaking changes to existing systems3. Format Options#
WDP supports two sequence format options:
3.1 Option 1: Numeric (RECOMMENDED)
Pattern: Exactly three digits (zero-padded)
Regex: [0-9]{3}
Range: 001 to 999
Examples:
001
002
003
...
9993.2 Option 2: Named (OPTIONAL)
Pattern: SCREAMING_SNAKE_CASE
Regex: [A-Z][A-Z0-9_]*
Examples:
MISSING
INVALID
EXPIRED
NOT_FOUND
TOKEN_REVOKEDImportant: Named sequences are display aliases only. They MUST be resolved to their numeric canonical form before hash computation.
4. Field Specification#
4.1 Field Properties
| Property | Value |
|---|---|
| Position | Fourth field (rightmost before compact ID) |
| Required | YES |
| Canonical Form | Numeric (001-999) |
| Display Form | Numeric OR Named (alias) |
| Hash Input | Always numeric canonical form |
4.2 Character Sets
Numeric sequences:
- ASCII digits only: 0-9
- Exactly 3 characters
- Zero-padded (e.g., 001, not 1)
Named sequences:
- ASCII uppercase letters: A-Z
- ASCII digits: 0-9
- Underscore: _
- Must start with uppercase letter
- No maximum length (but keep reasonable, 8-24 chars recommended)
5. Numeric Sequences (Canonical)#
5.1 Overview
Numeric sequences are the canonical identity for WDP error codes.
Range: 001 to 999 (999 possible sequences per Component.Primary)
Format: Exactly three digits, zero-padded
Examples:
001, 002, 003, ..., 098, 099, 100, ..., 999Important: The zero-padding is REQUIRED and is part of the canonical identity used for hash computation.
5.2 Benefits
Numeric sequences provide:
- ✅ Simple allocation - Easy to increment and assign
- ✅ Clear ordering - Natural sequence for related errors
- ✅ Stable identity - Can change associated names without affecting hash
- ✅ Compact display - Short and readable
- ✅ Convention support - Works well with semantic conventions (001=MISSING, etc.)
- ✅ Internationalization - Language-independent canonical form
5.3 Range Sufficiency
999 sequences per Component.Primary is sufficient for most use cases:
| Component Size | Typical Error Count |
|---|---|
| Small | 5-20 errors |
| Medium | 20-50 errors |
| Large | 50-100 errors |
| Very Large | 100-500 errors (rare) |
If you need more than 999 errors in a single Component.Primary:
This is often a design smell. Consider splitting into more specific subcategories:
Instead of:
E.Parser.*.001-999 (too many in one Primary)
Consider:
E.Parser.Syntax.001-150
E.Parser.Semantic.001-100
E.Parser.Lexical.001-50
E.Parser.Type.001-805.4 Format Rules
MUST:
- Use exactly three digits
- Zero-pad values less than 100 (e.g., 001, not 1)
- Stay within range 001-999
MUST NOT:
- Use 000 (reserved/invalid)
- Exceed 999
- Omit leading zeros
Hash Input: When computing the compact ID hash, the numeric sequence MUST be zero-padded:
✅ Correct: E.AUTH.TOKEN.001 (hash input)
❌ Incorrect: E.AUTH.TOKEN.1 (missing padding)6. Named Sequences (Display Aliases)#
6.1 Overview
Named sequences are OPTIONAL display aliases for numeric sequences. They provide semantic meaning in code and documentation but are NOT used for hash computation.
Key Principle: Named sequences MUST resolve to numeric sequences before hashing.
6.2 Format Rules
Pattern: SCREAMING_SNAKE_CASE
Regex: [A-Z][A-Z0-9_]*
Rules:
- Must start with uppercase letter (A-Z)
- May contain uppercase letters, digits, and underscores
- No lowercase letters, hyphens, or other special characters
- Recommended length: 8-24 characters
Examples:
MISSING ✅
INVALID ✅
EXPIRED ✅
NOT_FOUND ✅
TOKEN_REVOKED ✅
ALREADY_EXISTS ✅
PERMISSION_DENIED ✅Invalid:
missing ❌ Must be uppercase
Missing ❌ Must be SCREAMING_SNAKE_CASE
not-found ❌ No hyphens allowed
NOT FOUND ❌ No spaces allowed
_MISSING ❌ Must start with letter6.3 Relationship to Numeric
Canonical Identity: Numeric sequences (001-999)
Associated Name: Semantic alias for documentation
Display: Can show numeric, name, or both
Hash Input: Always numeric
Example:
Canonical: E.Auth.Token.001
Associated Name: TOKEN_MISSING
Display Forms: E.Auth.Token.001
E.Auth.Token.TOKEN_MISSING
E.Auth.Token.001 (TOKEN_MISSING)
Hash Input: E.AUTH.TOKEN.001 (always numeric)
Hash Output: Xy8Qz6.4 Benefits
Named sequences provide:
- ✅ Self-documenting - Semantic meaning visible in code
- ✅ Memorable - Easier to remember than numbers
- ✅ Readable - Clear intent in logs and error messages
- ✅ Code constants - Natural fit for programming language constants
6.5 Rationale for Resolution
Why resolve named → numeric before hashing?
- Stable Identity: Renaming aliases doesn't change the hash
- i18n Ready: Names can be localized while numeric identity remains universal
- Simplicity: Hash input contains only alphanumeric + period (no underscores)
- Flexibility: Names are optional and implementation-defined
- Deterministic: Same numeric sequence always produces same hash
7. Hash Canonical Form#
7.1 Hash Input Normalization
Before computing the compact ID hash, the sequence field MUST be:
- Resolved to numeric (if named alias is used)
- Part of uppercase-normalized full code
7.2 Normalization Flow
Numeric sequence (no resolution needed):
Display: E.Auth.Token.001
↓ normalize to uppercase
Hash Input: E.AUTH.TOKEN.001 (note: 001 with zero-padding)
↓ hash (xxHash64 → Base62)
Compact ID: Xy8QzKey Point: The numeric sequence in the hash input MUST be zero-padded to 3 digits:
- Sequence 1 → Hash input uses 001
- Sequence 42 → Hash input uses 042
- Sequence 123 → Hash input uses 123
Named sequence (resolve first):
Display: E.Auth.Token.TOKEN_MISSING
↓ resolve named → numeric
Resolved: E.Auth.Token.001
↓ normalize to uppercase
Hash Input: E.AUTH.TOKEN.001
↓ hash (xxHash64 → Base62)
Compact ID: Xy8Qz (same as numeric!)7.3 Key Properties
Same numeric sequence → Same hash:
E.Auth.Token.001 → Xy8Qz
E.Auth.Token.TOKEN_MISSING → Xy8Qz (resolves to 001)
E.Auth.Token.MISSING → Xy8Qz (resolves to 001)
All produce the same hash because they all resolve to:
E.AUTH.TOKEN.001 (with zero-padded 001)Zero-padding matters for hash computation:
✅ E.AUTH.TOKEN.001 → Xy8Qz (correct format)
❌ E.AUTH.TOKEN.1 → ??? (invalid - different hash, don't use!)Different numeric sequences → Different hashes:
E.Auth.Token.001 → Xy8Qz
E.Auth.Token.003 → Ab3Cd
E.Auth.Token.018 → Mn7Pq8. Choosing Between Formats#
8.1 Decision Matrix
| Consideration | Numeric | Named |
|---|---|---|
| Canonical identity | ✅ Yes | ❌ No (must resolve) |
| Hash input | ✅ Direct | ⚠️ Requires resolution |
| Simple allocation | ✅ Easy increment | ⚠️ Requires naming |
| Self-documenting | ⚠️ Needs docs | ✅ Semantic meaning |
| Code readability | ⚠️ Numbers cryptic | ✅ Clear intent |
| Internationalization | ✅ Universal | ⚠️ Language-specific |
| Stable hash | ✅ Rename-safe | ✅ (if resolved) |
8.2 Recommendation: Hybrid Approach
Best Practice: Use numeric sequences as canonical identity and associate semantic names for documentation and code constants.
Pattern:
// Define named constants that map to numeric values
const TOKEN_MISSING: u32 = 001;
const TOKEN_EXPIRED: u32 = 002;
const TOKEN_INVALID: u32 = 003;
// Use in code
fn create_error() -> String {
format!("E.Auth.Token.{:03}", TOKEN_MISSING)
// Result: "E.Auth.Token.001"
}Catalog documents both:
{
"E.Auth.Token.001": {
"name": "TOKEN_MISSING",
"compact_id": "Xy8Qz",
"message": "Token missing from Authorization header"
},
"E.Auth.Token.018": {
"name": "TOKEN_EXPIRED",
"compact_id": "Ab3Cd",
"message": "Token has expired"
}
}8.3 Pure Numeric vs Pure Named vs Hybrid
Pure Numeric:
E.Auth.Token.001
E.Auth.Token.018
E.Auth.Token.003- ✅ Simple
- ✅ Direct hash computation
- ⚠️ Requires external documentation
Pure Named:
E.Auth.Token.MISSING
E.Auth.Token.EXPIRED
E.Auth.Token.INVALID- ✅ Self-documenting
- ⚠️ Requires resolution mechanism
- ⚠️ More complex implementation
Hybrid (RECOMMENDED):
Code: const TOKEN_MISSING = 001;
Display: E.Auth.Token.001
Alias: TOKEN_MISSING
Catalog: Maps 001 ↔ TOKEN_MISSING
Hash: Always from 001- ✅ Best of both worlds
- ✅ Flexible display
- ✅ Stable identity
8.4 Non-Conventional Usage (DISCOURAGED)
While WDP sequence format is normative, sequence semantic conventions are informative. Projects MAY choose not to follow the conventions defined in 7-SEQUENCE-CONVENTIONS.md, though this is DISCOURAGED.
Example of non-conventional usage:
// ❌ DISCOURAGED: Sequential numbering without semantic meaning
E.Auth.Token.001 - Token missing
E.Auth.Token.002 - Token malformed
E.Auth.Token.003 - Token invalid signature
E.Auth.Token.004 - Token expired
E.Auth.Token.005 - Token not yet validRecommended conventional alternative:
// ✅ RECOMMENDED: Semantic numbering following conventions
E.Auth.Token.001 - Token missing (001 = MISSING)
E.Auth.Token.002 - Token type mismatch (002 = MISMATCH)
E.Auth.Token.003 - Token invalid signature (003 = INVALID)
E.Auth.Token.018 - Token expired (018 = STALE)
E.Auth.Token.018 - Token not yet valid (018 = STALE)Why follow conventions?
- Consistency across projects and organizations
- Predictable error patterns for developers
- Easier debugging and error recognition
- Interoperability between systems
See 7-SEQUENCE-CONVENTIONS.md for complete semantic guidance.
9. Implementation Patterns#
9.1 Pattern A: Code Constants (Recommended)
// Named constants resolve to numeric values
pub const TOKEN_MISSING: u32 = 001;
pub const TOKEN_EXPIRED: u32 = 018;
pub const TOKEN_INVALID: u32 = 003;
pub fn format_error_code(seq: u32) -> String {
format!("E.Auth.Token.{:03}", seq)
}
// Usage
let code = format_error_code(TOKEN_MISSING);
// Result: "E.Auth.Token.001"9.2 Pattern B: Catalog Mapping
{
"sequences": {
"001": {
"name": "TOKEN_MISSING",
"message": "Token missing from Authorization header"
},
"018": {
"name": "TOKEN_EXPIRED",
"message": "Token has expired"
}
}
}9.3 Pattern C: Enum with Numeric Values
#[repr(u32)]
pub enum TokenError {
Missing = 001,
Expired = 018,
Invalid = 003,
}
impl TokenError {
pub fn to_code(&self) -> String {
format!("E.Auth.Token.{:03}", *self as u32)
}
}
// Usage
let code = TokenError::Missing.to_code();
// Result: "E.Auth.Token.001"9.4 Pattern D: Resolution Function
pub fn resolve_sequence(seq: &str) -> String {
match seq {
"MISSING" | "TOKEN_MISSING" => "001".to_string(),
"EXPIRED" | "TOKEN_EXPIRED" => "002".to_string(),
"INVALID" | "TOKEN_INVALID" => "003".to_string(),
// If already numeric, pass through
s if s.chars().all(|c| c.is_ascii_digit()) => s.to_string(),
_ => panic!("Unknown sequence: {}", seq),
}
}
// Usage
let numeric = resolve_sequence("TOKEN_MISSING");
// Result: "001"10. Validation Rules#
10.1 Numeric Sequence Validation
Implementations MUST validate that numeric sequences:
- Contain exactly 3 ASCII digits
- Are within range 001-999 (000 is invalid)
- Are zero-padded (e.g., 001, not 1)
Regex:
^(0[0-9]{2}|[1-9][0-9]{2})$Explanation:
- 0[0-9]{2} - 001-099
- [1-9][0-9]{2} - 100-999
Simplified (if range checking separately):
^[0-9]{3}$Then check: 001 ≤ value ≤ 999
10.2 Named Sequence Validation
Implementations MUST validate that named sequences:
- Start with uppercase ASCII letter
- Contain only uppercase letters, digits, and underscores
- Do not start with underscore or digit
Regex:
^[A-Z][A-Z0-9_]*$10.3 Validation Examples
Valid Numeric:
001 ✅
042 ✅
100 ✅
999 ✅Invalid Numeric:
000 ❌ Reserved/invalid
1 ❌ Must be 3 digits (should be 001)
01 ❌ Must be 3 digits (should be 001)
1000 ❌ Exceeds 999
ABC ❌ Not numericValid Named:
MISSING ✅
TOKEN_EXPIRED ✅
NOT_FOUND ✅
ALREADY_EXISTS ✅
HTTP_404 ✅Invalid Named:
missing ❌ Must be uppercase
Token_Expired ❌ Must be all uppercase
not-found ❌ No hyphens
_MISSING ❌ Cannot start with underscore
404_HTTP ❌ Cannot start with digit11. Examples#
11.1 Numeric Sequences
Authentication errors:
E.Auth.Token.001 - Token missing (001 = MISSING)
E.Auth.Token.002 - Token type mismatch (002 = MISMATCH)
E.Auth.Token.003 - Token invalid signature (003 = INVALID)
E.Auth.Token.018 - Token expired (018 = STALE)
E.Auth.Token.031 - Custom token validation errorDatabase errors:
E.Database.Connection.001 - Connection string missing
E.Database.Connection.017 - Connection timeout
E.Database.Connection.021 - Database not found
E.Database.Connection.027 - Database unavailable11.2 Named Sequences (Aliases)
Authentication errors:
E.Auth.Token.MISSING - Alias for 001
E.Auth.Token.MALFORMED - Alias for 002
E.Auth.Token.INVALID - Alias for 003
E.Auth.Token.EXPIRED - Alias for 004
E.Auth.Token.NOT_YET_VALID - Alias for 005Database errors:
E.Database.Connection.MISSING - Alias for 001
E.Database.Connection.TIMEOUT - Alias for 017
E.Database.Connection.NOT_FOUND - Alias for 021
E.Database.Connection.UNAVAILABLE - Alias for 02711.3 Hash Equivalence
All of these produce the same compact ID:
E.Auth.Token.001 → Hash input: E.AUTH.TOKEN.001 → Xy8Qz
E.Auth.Token.MISSING → Resolves to 001 → E.AUTH.TOKEN.001 → Xy8Qz
E.Auth.Token.TOKEN_MISSING → Resolves to 001 → E.AUTH.TOKEN.001 → Xy8Qz
E.Auth.Token.018 → Hash input: E.AUTH.TOKEN.018 → Ab3Cd
E.Auth.Token.EXPIRED → Resolves to 018 → E.AUTH.TOKEN.018 → Ab3Cd11.4 Mixed Display Forms
Within the same project:
E.Auth.Token.001 - Numeric (canonical)
E.Parser.Syntax.UNEXPECTED - Named (alias, resolves to numeric)
E.Database.Query.031 - Numeric (canonical, project-specific)
E.Network.Timeout.017 - Numeric (017 = TIMEOUT, conventional)All named forms resolve to numeric before hashing.
11.5 Complete Examples with Catalogs
Error Code:
E.Auth.Token.001
E.Auth.Token.018
E.Auth.Token.003Catalog Entry:
{
"E.Auth.Token.001": {
"sequence_name": "TOKEN_MISSING",
"compact_id": "Xy8Qz",
"message": "Authentication token missing from request",
"severity": "E",
"component": "Auth",
"primary": "Token",
"sequence": "001",
"tone": "negative",
"priority": 8,
"blocking": true
},
"E.Auth.Token.018": {
"sequence_name": "TOKEN_EXPIRED",
"compact_id": "Ab3Cd",
"message": "Authentication token has expired",
"severity": "E",
"component": "Auth",
"primary": "Token",
"sequence": "018",
"tone": "negative",
"priority": 8,
"blocking": true
}
}Display Options:
Compact: Xy8Qz
Short: E.Auth.Token.001
Named: E.Auth.Token.TOKEN_MISSING
Full: E.Auth.Token.001 -> Xy8Qz
Verbose: E.Auth.Token.001 (TOKEN_MISSING) -> Xy8Qz12. Common Questions (FAQ)#
Q1: How does "versioning" work with sequences?
A: The sequence field doesn't provide traditional API versioning. Instead, it enables catalog evolution - you can add new error codes over time without breaking existing ones.
Example:
Initial release (v1.0):
E.Auth.Token.001 - Token missing
E.Auth.Token.002 - Token expired
E.Auth.Token.003 - Token invalid
Later release (v1.1):
E.Auth.Token.001 - Token missing (unchanged)
E.Auth.Token.002 - Token expired (unchanged)
E.Auth.Token.003 - Token invalid (unchanged)
E.Auth.Token.004 - Token revoked (NEW)
E.Auth.Token.005 - Token blacklisted (NEW)Benefits:
- Existing codes (001-003) remain stable
- Hash values don't change
- Client catalogs can be updated incrementally
- No breaking changes for systems using old codes
Q2: Is the hash input E.AUTH.TOKEN.001 or E.AUTH.TOKEN.1?
A: The hash input is E.AUTH.TOKEN.001 (with zero-padding).
Why it matters:
Sequence 1 → Display: E.Auth.Token.001
→ Hash input: E.AUTH.TOKEN.001 (with 001)
→ Result: Xy8Qz
If you used E.AUTH.TOKEN.1 (no padding):
→ Result: DIFFERENT HASH (wrong!)Rule: Always use exactly 3 digits with zero-padding in the hash input:
- 1 → 001
- 42 → 042
- 123 → 123
Q3: Do I need to store the zero-padded format?
A: Yes, the canonical format is always zero-padded to 3 digits.
Correct:
const TOKEN_MISSING: &str = "001"; // Store as "001"
let code = format!("E.Auth.Token.{}", TOKEN_MISSING);
// Result: "E.Auth.Token.001"Incorrect:
const TOKEN_MISSING: u32 = 1; // Don't store as integer 1
let code = format!("E.Auth.Token.{}", TOKEN_MISSING);
// Result: "E.Auth.Token.1" ❌ WRONG!If you must use integers, always format with padding:
const TOKEN_MISSING: u32 = 1;
let code = format!("E.Auth.Token.{:03}", TOKEN_MISSING);
// Result: "E.Auth.Token.001" ✅ Correct!Q4: Can I skip sequences (e.g., 001, 002, 005)?
A: Yes, you can skip sequences. The numbers don't have to be consecutive.
Example:
E.Auth.Token.001 - Token missing
E.Auth.Token.002 - Token expired
E.Auth.Token.010 - Token deprecated (skipped 003-009)
E.Auth.Token.031 - Custom project error (skipped 011-030)This is useful for:
- Leaving room for future errors
- Grouping by semantic ranges (001-010 validation, 011-020 state, etc.)
- Maintaining consistency with conventions
Q5: What happens if I change a sequence number?
A: Changing a sequence number creates a completely new error code with a different hash.
Example:
Old: E.Auth.Token.001 → Hash: Xy8Qz
New: E.Auth.Token.042 → Hash: DIFFERENT!
→ This is a breaking change
→ Old catalogs won't recognize the new code
→ Logs and monitoring will treat them as separate errorsBest Practice: Never renumber existing sequences. Add new sequences instead.
Q6: Can named sequences contain numbers (e.g., TOKEN_404)?
A: Yes, named sequences can contain digits, but they must start with a letter.
Valid:
TOKEN_MISSING ✅
TOKEN_404 ✅ (starts with letter)
HTTP_401_ERROR ✅
OAUTH2_FAILED ✅Invalid:
404_TOKEN ❌ (starts with digit)
_TOKEN_MISSING ❌ (starts with underscore)Normative References#
- STRUCTURE.md - WDP error code structure overview
- 1-SEVERITY.md - Severity field specification
- 2-COMPONENT.md - Component field specification
- 3-PRIMARY.md - Primary field specification
- 5-COMPACT-IDS.md - Compact ID generation
- 7-SEQUENCE-CONVENTIONS.md - Sequence conventions (informative)
Informative References#
- RFC 2119 - Key words for RFCs
Conformance#
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.