How a simple library can revolutionize your error monitoring and debugging workflow
The Problem with Traditional Error Tracking
Picture this: You're debugging a production issue where users are reporting "User not found" errors. You check your error tracking dashboard and see hundreds of similar errors, but they all have different IDs:
-
Error ID: a1b2c3d4
- "User 123 not found" -
Error ID: e5f6g7h8
- "User 456 not found" -
Error ID: i9j0k1l2
- "User 789 not found"
Even though these are essentially the same error (a user lookup failure), your monitoring system treats them as completely different issues. This makes it nearly impossible to:
- Group related errors for effective analysis
- Track error frequency accurately
- Identify patterns in your application failures
- Prioritize fixes based on real impact
Enter StableError: The Solution
StableError is a TypeScript library that solves this exact problem by generating stable, consistent error IDs based on the semantic content of your errors, not their variable parts.
The Magic: Same Error = Same ID
import { createStableError } from 'stable-error';
// These all generate the SAME error ID
const error1 = createStableError('User 123 not found', {
category: 'validation',
metadata: { field: 'email' }
});
const error2 = createStableError('User 456 not found', {
category: 'validation',
metadata: { field: 'email' }
});
const error3 = createStableError('USER 789 NOT FOUND', {
category: 'validation',
metadata: { field: 'email' }
});
console.log(error1.id === error2.id); // true
console.log(error1.id === error3.id); // true
How It Works: Intelligent Message Normalization
StableError uses sophisticated message normalization to ensure that semantically identical errors produce the same ID:
1. Number Normalization
// All of these become "user NUMBER not found"
"User 123 not found"
"User 456 not found"
"User 999999 not found"
2. UUID Normalization
// All of these become "user UUID not found"
"User 550e8400-e29b-41d4-a716-446655440000 not found"
"User 6ba7b810-9dad-11d1-80b4-00c04fd430c8 not found"
3. Timestamp Normalization
// All of these become "error at TIMESTAMP"
"Error at 2023-01-01T10:00:00Z"
"Error at 2023-12-31T23:59:59Z"
"Error at 1672531200000" // Unix timestamp
4. Case and Whitespace Normalization
// All of these become "user not found"
"User not found"
"USER NOT FOUND"
" user not found "
Smart Metadata Filtering
Not all metadata should affect error ID generation. StableError only considers "stable" metadata keys that represent the error's semantic meaning:
Included in ID generation:
-
type
,code
,field
,operation
,service
,component
Ignored (variable data):
-
userId
,timestamp
,sessionId
,requestId
// These generate the SAME ID (userId and timestamp are ignored)
createStableError('Error', {
metadata: {
field: 'email',
userId: 123, // Ignored
timestamp: '2023-01-01' // Ignored
}
});
createStableError('Error', {
metadata: {
field: 'email',
userId: 456, // Ignored
timestamp: '2023-12-31' // Ignored
}
});
Real-World Usage Examples
1. API Error Handling
import { createStableError } from 'stable-error';
async function getUserById(id: string) {
try {
const user = await database.findUser(id);
if (!user) {
throw createStableError('User not found', {
category: 'validation',
statusCode: 404,
severity: 'medium',
metadata: {
field: 'userId',
operation: 'user_lookup'
}
});
}
return user;
} catch (error) {
// Convert any unexpected errors to stable errors
if (error instanceof Error && !error.id) {
throw createStableError(error, {
category: 'database',
severity: 'high',
metadata: { operation: 'user_lookup' }
});
}
throw error;
}
}
2. Error Monitoring Integration
// With your favorite error tracking service
import { createStableError } from 'stable-error';
function trackError(error: Error, context: any) {
const stableError = createStableError(error, {
category: 'api',
severity: 'high',
metadata: {
service: 'user-service',
component: 'auth-middleware'
}
});
// Send to your monitoring service
errorTracker.captureException(stableError, {
tags: {
errorId: stableError.id,
category: stableError.category,
severity: stableError.severity
},
extra: stableError.metadata
});
}
3. Error Aggregation Dashboard
// Group errors by stable ID for analytics
const errorGroups = errors.reduce((groups, error) => {
const stableError = createStableError(error);
const id = stableError.id;
if (!groups[id]) {
groups[id] = {
id,
message: stableError.message,
category: stableError.category,
count: 0,
firstSeen: stableError.timestamp,
lastSeen: stableError.timestamp,
severity: stableError.severity
};
}
groups[id].count++;
groups[id].lastSeen = stableError.timestamp;
return groups;
}, {});
Key Features
✅ Stable Error IDs
- Same error message + category + metadata = Same ID
- 8-character hexadecimal IDs for easy reference
✅ Full TypeScript Support
- Complete type safety with comprehensive interfaces
- IntelliSense support for all options and methods
✅ Flexible Input
- Works with string messages or existing Error objects
- Preserves original stack traces when converting
✅ Rich Error Information
- Category classification
- Severity levels (low, medium, high, critical)
- HTTP status codes
- Timestamps
- Custom metadata
✅ JSON Serialization
const error = createStableError('Test error', {
category: 'validation',
severity: 'high'
});
const json = error.toJSON();
// {
// id: "a1b2c3d4",
// message: "Test error",
// category: "validation",
// severity: "high",
// timestamp: "2023-01-01T10:00:00Z",
// statusCode: 500,
// metadata: {},
// stack: "Error: Test error\n at ..."
// }
Installation and Quick Start
npm install stable-error
# or
yarn add stable-error
# or
bun add stable-error
import { createStableError } from 'stable-error';
// Create your first stable error
const error = createStableError('Something went wrong', {
category: 'api',
severity: 'high',
metadata: { endpoint: '/users', method: 'GET' }
});
console.log(`Error ID: ${error.id}`); // Always the same for this error type
Browser and Runtime Support
- Modern browsers (ES2018+)
- Node.js 14+
- TypeScript 4.5+
- Bun (fully tested)
Conclusion
StableError transforms error tracking from a chaotic mess into an organized, actionable system. By generating consistent IDs for semantically identical errors, it enables:
- Better error analytics - See which errors actually matter
- Faster debugging - Group related issues together
- Improved prioritization - Focus on high-frequency problems
- Cleaner dashboards - No more noise from variable data
Whether you're building a small application or managing a large-scale system, StableError provides the foundation for effective error monitoring and debugging. Give it a try and see how it can revolutionize your error tracking workflow.
Ready to get started? Check out the GitHub repository for full documentation, examples, and the latest updates.
StableError is open source and available under the MIT license.
Top comments (0)