The Linked Errors integration automatically captures error cause chains and aggregate errors, following the error’s cause property or custom error linking keys.
Installation
This integration is enabled by default in all Sentry SDKs.
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: 'your-dsn',
// linkedErrorsIntegration is included by default
});
How It Works
The integration captures linked errors in two ways:
1. Error Cause Chain (ECMAScript 2022)
Automatic capture of errors linked via the cause property:
try {
try {
throw new Error('Database connection failed');
} catch (dbError) {
throw new Error('Failed to fetch user data', { cause: dbError });
}
} catch (error) {
Sentry.captureException(error);
// Captures both errors:
// 1. Failed to fetch user data
// 2. Database connection failed (as linked error)
}
2. Aggregate Errors
Captures all errors from AggregateError:
const errors = [
new Error('Failed to load user profile'),
new Error('Failed to load user settings'),
new Error('Failed to load user preferences'),
];
const aggregateError = new AggregateError(
errors,
'Failed to load user data'
);
Sentry.captureException(aggregateError);
// Captures all 4 errors with proper linking
Configuration
Default Configuration
The integration uses these defaults:
Sentry.init({
dsn: 'your-dsn',
integrations: [
Sentry.linkedErrorsIntegration({
key: 'cause', // Property to follow for linked errors
limit: 5, // Maximum number of linked errors to capture
}),
],
});
Custom Error Key
If your application uses a custom property for error chaining:
class CustomError extends Error {
constructor(message, previousError) {
super(message);
this.previousError = previousError;
}
}
Sentry.init({
dsn: 'your-dsn',
integrations: [
Sentry.linkedErrorsIntegration({
key: 'previousError', // Follow custom property
limit: 10,
}),
],
});
Limit Linked Errors
Control the maximum depth of error chains:
Sentry.init({
dsn: 'your-dsn',
integrations: [
Sentry.linkedErrorsIntegration({
limit: 3, // Only capture up to 3 linked errors
}),
],
});
Configuration Options
The property name to follow for linked errors
Maximum number of linked errors to capture
Practical Examples
Service Layer Error Chaining
class DatabaseService {
async getUser(id) {
try {
return await db.query('SELECT * FROM users WHERE id = ?', [id]);
} catch (error) {
throw new Error(`Database query failed for user ${id}`, {
cause: error,
});
}
}
}
class UserService {
constructor(dbService) {
this.db = dbService;
}
async getUserProfile(id) {
try {
const user = await this.db.getUser(id);
return this.formatProfile(user);
} catch (error) {
throw new Error('Failed to load user profile', { cause: error });
}
}
}
// When captured, shows full error chain:
// 1. Failed to load user profile
// 2. Database query failed for user 123
// 3. Connection timeout (original DB error)
Parallel Operations with AggregateError
async function loadUserData(userId) {
const promises = [
fetchProfile(userId),
fetchSettings(userId),
fetchPreferences(userId),
fetchHistory(userId),
];
const results = await Promise.allSettled(promises);
const errors = results
.filter(r => r.status === 'rejected')
.map(r => r.reason);
if (errors.length > 0) {
const error = new AggregateError(
errors,
`Failed to load ${errors.length} data sources for user ${userId}`
);
Sentry.captureException(error);
// All individual errors are captured and linked
}
}
API Integration Error Context
class APIClient {
async request(endpoint, options) {
try {
const response = await fetch(endpoint, options);
if (!response.ok) {
const errorData = await response.json();
const apiError = new Error(
`API Error: ${errorData.message}`,
);
apiError.status = response.status;
apiError.data = errorData;
throw apiError;
}
return await response.json();
} catch (error) {
throw new Error(`Failed to call ${endpoint}`, { cause: error });
}
}
}
// Error chain shows:
// 1. Failed to call /api/users/123
// 2. API Error: User not found (with status and data)
How Linked Errors Appear in Sentry
In the Sentry UI, linked errors appear as:
- Main Exception: The top-level error you captured
- Chained Exceptions: Related errors shown below, marked as “caused by”
- Full Context: Each error includes its own stacktrace and metadata
Source Code
The Linked Errors integration is implemented in:
packages/core/src/integrations/linkederrors.ts:16
- The integration limits error chains to prevent excessive data
- Each linked error includes its full stacktrace
- Consider the
limit option for deeply nested error chains
Best Practices
Use error causes to preserve context when re-throwing errors
-
Always preserve the original error:
// Good
throw new Error('Operation failed', { cause: originalError });
// Bad - loses original context
throw new Error('Operation failed');
-
Add context at each layer:
catch (error) {
throw new Error(`Failed to process order ${orderId}`, {
cause: error,
});
}
-
Use AggregateError for parallel operations:
const results = await Promise.allSettled(operations);
const errors = results
.filter(r => r.status === 'rejected')
.map(r => r.reason);
if (errors.length > 0) {
throw new AggregateError(errors, 'Some operations failed');
}
Troubleshooting
Linked Errors Not Appearing
Ensure you’re using the correct property name:
// Check your error structure
console.log(error.cause); // Should contain the linked error
// Or configure custom key
Sentry.linkedErrorsIntegration({ key: 'innerError' });
Too Many Linked Errors
Increase the limit if error chains are being truncated:
Sentry.linkedErrorsIntegration({ limit: 10 });