0
# Error Handling
1
2
Comprehensive error tracking with custom attributes, error grouping, and expected error marking.
3
4
## Capabilities
5
6
### Notice Error
7
8
Record errors that have been handled by your application code.
9
10
```javascript { .api }
11
/**
12
* Send errors to New Relic that you've already handled. Does not obey
13
* ignore_status_codes configuration.
14
* @param {Error|string} error - The error to be traced
15
* @param {object} [customAttributes] - Optional custom attributes for the error
16
* @param {boolean} [expected] - Whether the error is expected (defaults to false)
17
* @returns {false|undefined} Returns false when disabled/errored, undefined on success
18
*/
19
function noticeError(error, customAttributes, expected);
20
```
21
22
**Usage Examples:**
23
24
```javascript
25
const newrelic = require('newrelic');
26
27
// Basic error reporting
28
try {
29
performSomeTask();
30
} catch (err) {
31
newrelic.noticeError(err);
32
// Handle the error in your application
33
res.status(500).json({ error: 'Internal server error' });
34
}
35
36
// Error with custom attributes
37
try {
38
processPayment(paymentData);
39
} catch (err) {
40
newrelic.noticeError(err, {
41
userId: user.id,
42
paymentMethod: paymentData.method,
43
amount: paymentData.amount,
44
orderContext: 'checkout'
45
});
46
throw err; // Re-throw if needed
47
}
48
49
// Expected errors (tracked but don't affect error rate)
50
try {
51
validateUserInput(input);
52
} catch (validationError) {
53
newrelic.noticeError(validationError, {
54
inputType: 'userRegistration',
55
validationRule: validationError.rule
56
}, true); // Mark as expected
57
58
return res.status(400).json({
59
error: 'Validation failed',
60
details: validationError.message
61
});
62
}
63
64
// String errors (automatically converted to Error objects)
65
if (user.permissions < requiredLevel) {
66
newrelic.noticeError('Insufficient permissions', {
67
userId: user.id,
68
requiredLevel: requiredLevel,
69
userLevel: user.permissions
70
});
71
}
72
```
73
74
### Set Error Group Callback
75
76
Define custom logic for grouping similar errors together in New Relic's Errors Inbox.
77
78
```javascript { .api }
79
/**
80
* Set a custom callback function to generate error group names for the Errors Inbox.
81
* The callback receives error metadata and must return a string.
82
* @param {Function} callback - Synchronous function to generate error.group.name attribute
83
*/
84
function setErrorGroupCallback(callback);
85
```
86
87
**Usage Examples:**
88
89
```javascript
90
// Basic error grouping by error type and HTTP status
91
newrelic.setErrorGroupCallback((metadata) => {
92
const errorType = metadata.error?.name || 'UnknownError';
93
const statusCode = metadata['http.statusCode'];
94
95
if (statusCode) {
96
return `${errorType}-${statusCode}`;
97
}
98
99
return errorType;
100
});
101
102
// Advanced error grouping with business context
103
newrelic.setErrorGroupCallback((metadata) => {
104
const error = metadata.error;
105
const customAttrs = metadata.customAttributes || {};
106
107
// Group validation errors by the field that failed
108
if (error?.name === 'ValidationError' && customAttrs.field) {
109
return `ValidationError-${customAttrs.field}`;
110
}
111
112
// Group payment errors by payment method
113
if (error?.message?.includes('payment') && customAttrs.paymentMethod) {
114
return `PaymentError-${customAttrs.paymentMethod}`;
115
}
116
117
// Group API errors by endpoint
118
if (metadata['request.uri'] && error?.name === 'APIError') {
119
const endpoint = metadata['request.uri'].split('?')[0]; // Remove query params
120
return `APIError-${endpoint}`;
121
}
122
123
// Default grouping
124
return error?.name || 'UnknownError';
125
});
126
127
// Error grouping with user context
128
newrelic.setErrorGroupCallback((metadata) => {
129
const error = metadata.error;
130
const isExpected = metadata['error.expected'];
131
const userTier = metadata.customAttributes?.userTier;
132
133
// Don't group expected errors with unexpected ones
134
const prefix = isExpected ? 'Expected' : 'Unexpected';
135
136
// Group by user tier for business insights
137
if (userTier) {
138
return `${prefix}-${error?.name}-${userTier}`;
139
}
140
141
return `${prefix}-${error?.name || 'UnknownError'}`;
142
});
143
```
144
145
## Error Metadata Structure
146
147
The error group callback receives a metadata object with the following structure:
148
149
```javascript { .api }
150
interface ErrorMetadata {
151
/** Custom attributes passed to noticeError() */
152
customAttributes?: object;
153
/** HTTP request URI */
154
'request.uri'?: string;
155
/** HTTP status code */
156
'http.statusCode'?: string;
157
/** HTTP method */
158
'http.method'?: string;
159
/** The actual Error object */
160
error?: Error;
161
/** Whether the error was marked as expected */
162
'error.expected'?: boolean;
163
}
164
```
165
166
## Configuration and Security
167
168
### High Security Mode
169
170
When high security mode is enabled:
171
- Custom attributes in `noticeError()` are ignored and logged as debug message
172
- The error itself is still recorded
173
174
### Configuration Controls
175
176
- `api.notice_error_enabled: false` - Disables error recording
177
- `api.custom_attributes_enabled: false` - Disables custom attributes on errors
178
179
## Common Usage Patterns
180
181
### API Error Handling with Context
182
183
```javascript
184
app.use('/api', (req, res, next) => {
185
try {
186
next();
187
} catch (error) {
188
const errorContext = {
189
endpoint: req.path,
190
method: req.method,
191
userId: req.user?.id,
192
userAgent: req.get('User-Agent'),
193
ipAddress: req.ip,
194
requestId: req.headers['x-request-id']
195
};
196
197
newrelic.noticeError(error, errorContext);
198
199
res.status(500).json({
200
error: 'Internal server error',
201
requestId: errorContext.requestId
202
});
203
}
204
});
205
```
206
207
### Database Error Handling
208
209
```javascript
210
async function performDatabaseOperation(query, params) {
211
try {
212
return await database.execute(query, params);
213
} catch (error) {
214
const errorContext = {
215
queryType: query.type,
216
tableCount: query.tables?.length || 0,
217
paramCount: params.length,
218
connectionPool: database.pool.name
219
};
220
221
// Mark timeout errors as expected if they're common
222
const isExpected = error.code === 'TIMEOUT' && query.type === 'SELECT';
223
224
newrelic.noticeError(error, errorContext, isExpected);
225
throw error;
226
}
227
}
228
```
229
230
### Validation Error Handling
231
232
```javascript
233
function validateUserData(userData, schema) {
234
try {
235
return schema.validate(userData);
236
} catch (validationError) {
237
// These are expected business logic errors
238
newrelic.noticeError(validationError, {
239
validationType: 'userRegistration',
240
failedField: validationError.field,
241
providedValue: validationError.value,
242
validationRule: validationError.rule
243
}, true); // Mark as expected
244
245
throw validationError;
246
}
247
}
248
```
249
250
### External Service Error Handling
251
252
```javascript
253
async function callExternalAPI(endpoint, data) {
254
try {
255
const response = await httpClient.post(endpoint, data);
256
return response.data;
257
} catch (error) {
258
const errorContext = {
259
externalService: 'payment-processor',
260
endpoint: endpoint,
261
httpStatusCode: error.response?.status,
262
retryAttempt: data.retryCount || 0,
263
requestSize: JSON.stringify(data).length
264
};
265
266
// 4xx errors are typically expected (bad requests)
267
const isExpected = error.response?.status >= 400 && error.response?.status < 500;
268
269
newrelic.noticeError(error, errorContext, isExpected);
270
throw error;
271
}
272
}
273
```
274
275
### Business Logic Error Grouping
276
277
```javascript
278
// Set up comprehensive error grouping
279
newrelic.setErrorGroupCallback((metadata) => {
280
const error = metadata.error;
281
const customAttrs = metadata.customAttributes || {};
282
const uri = metadata['request.uri'] || '';
283
const statusCode = metadata['http.statusCode'];
284
285
// Payment processing errors
286
if (customAttrs.paymentMethod || uri.includes('/payment')) {
287
return `Payment-${customAttrs.paymentMethod || 'Unknown'}-${error?.name}`;
288
}
289
290
// User authentication errors
291
if (uri.includes('/auth') || error?.name === 'AuthenticationError') {
292
return `Auth-${error?.name}-${statusCode}`;
293
}
294
295
// Data validation errors grouped by entity type
296
if (error?.name === 'ValidationError' && customAttrs.entityType) {
297
return `Validation-${customAttrs.entityType}-${customAttrs.failedField || 'Unknown'}`;
298
}
299
300
// Third-party service errors
301
if (customAttrs.externalService) {
302
return `External-${customAttrs.externalService}-${error?.name}`;
303
}
304
305
// Database errors grouped by operation type
306
if (error?.name?.includes('Database') && customAttrs.queryType) {
307
return `Database-${customAttrs.queryType}-${error?.name}`;
308
}
309
310
// Default grouping by error name and status code
311
return `${error?.name || 'UnknownError'}${statusCode ? `-${statusCode}` : ''}`;
312
});
313
```
314
315
### Error Rate Monitoring
316
317
```javascript
318
// Monitor error rates and alert on thresholds
319
let errorCount = 0;
320
let totalRequests = 0;
321
322
app.use((req, res, next) => {
323
totalRequests++;
324
325
res.on('finish', () => {
326
if (res.statusCode >= 500) {
327
errorCount++;
328
329
// Calculate error rate every 100 requests
330
if (totalRequests % 100 === 0) {
331
const errorRate = errorCount / totalRequests;
332
333
newrelic.recordMetric('ErrorRate/Server', errorRate);
334
335
if (errorRate > 0.05) { // Alert if > 5% error rate
336
newrelic.recordCustomEvent('HighErrorRate', {
337
errorRate: errorRate,
338
errorCount: errorCount,
339
totalRequests: totalRequests,
340
alertLevel: 'critical'
341
});
342
}
343
}
344
}
345
});
346
347
next();
348
});
349
```
350
351
### Async Error Handling
352
353
```javascript
354
// Proper async error handling with context preservation
355
async function processAsyncTask(taskData) {
356
const taskContext = {
357
taskId: taskData.id,
358
taskType: taskData.type,
359
userId: taskData.userId,
360
priority: taskData.priority
361
};
362
363
try {
364
const result = await performAsyncOperation(taskData);
365
return result;
366
} catch (error) {
367
// Preserve context when handling async errors
368
newrelic.noticeError(error, {
369
...taskContext,
370
errorOccurredAt: 'asyncTaskProcessing',
371
asyncContext: true
372
});
373
374
// Re-throw to maintain error flow
375
throw error;
376
}
377
}
378
379
// Handling unhandled promise rejections
380
process.on('unhandledRejection', (reason, promise) => {
381
newrelic.noticeError(reason instanceof Error ? reason : new Error(String(reason)), {
382
source: 'unhandledRejection',
383
promiseLocation: promise.toString()
384
});
385
});
386
```