Proper error handling ensures you capture actionable information while avoiding noise and maintaining application stability.
Capturing Errors
Automatic Error Capture
Sentry automatically captures unhandled errors and promise rejections:
import * as Sentry from '@sentry/browser' ;
Sentry . init ({
dsn: '__DSN__' ,
integrations: [
// Enabled by default
Sentry . globalHandlersIntegration (),
],
});
// These are captured automatically:
throw new Error ( 'Uncaught error' );
Promise . reject ( new Error ( 'Unhandled rejection' ));
window . addEventListener ( 'error' , ( event ) => {
// Captured automatically
});
Manual Error Capture
Capture errors in try-catch blocks:
try {
riskyOperation ();
} catch ( error ) {
Sentry . captureException ( error );
// Optionally rethrow
throw error ;
}
Always capture errors before rethrowing them to ensure they’re sent to Sentry even if caught by another handler.
Capturing Messages
For non-error events, use captureMessage:
// Warning level
Sentry . captureMessage ( 'Something unusual happened' , 'warning' );
// Info level
Sentry . captureMessage ( 'User performed action' , 'info' );
// Error level
Sentry . captureMessage ( 'Critical system state' , 'error' );
Adding Context
User Context
Identify users to track who experiences errors:
Sentry . setUser ({
id: '12345' ,
username: 'john_doe' ,
email: '[email protected] ' ,
ip_address: '{{auto}}' , // Auto-detect IP
});
// Clear user on logout
Sentry . setUser ( null );
Be mindful of privacy regulations (GDPR, CCPA) when setting user data. Only include PII if you have user consent.
Add searchable key-value pairs:
// Set tags globally
Sentry . setTags ({
environment: 'production' ,
feature: 'checkout' ,
});
// Set a single tag
Sentry . setTag ( 'page_locale' , 'en-US' );
// Set tags for a specific error
Sentry . withScope (( scope ) => {
scope . setTag ( 'section' , 'payment' );
Sentry . captureException ( new Error ( 'Payment failed' ));
});
Add structured data to events:
Sentry . setContext ( 'character' , {
name: 'Mighty Fighter' ,
level: 42 ,
class: 'warrior' ,
});
// For a specific error
Sentry . withScope (( scope ) => {
scope . setContext ( 'order' , {
id: 'order-123' ,
items: 5 ,
total: 99.99 ,
});
Sentry . captureException ( new Error ( 'Order processing failed' ));
});
Fingerprinting
Control how errors are grouped:
Sentry . withScope (( scope ) => {
scope . setFingerprint ([ '{{ default }}' , 'custom-group' ]);
Sentry . captureException ( error );
});
// Dynamic fingerprinting based on error
try {
await apiCall ();
} catch ( error ) {
Sentry . withScope (( scope ) => {
if ( error . code === 'RATE_LIMIT' ) {
scope . setFingerprint ([ 'rate-limit' , error . endpoint ]);
} else {
scope . setFingerprint ([ '{{ default }}' ]);
}
Sentry . captureException ( error );
});
}
Breadcrumbs
Breadcrumbs provide a trail of events leading to an error:
// Manual breadcrumbs
Sentry . addBreadcrumb ({
category: 'auth' ,
message: 'User logged in' ,
level: 'info' ,
});
Sentry . addBreadcrumb ({
category: 'api' ,
message: 'Fetching user profile' ,
level: 'info' ,
data: {
url: '/api/profile' ,
method: 'GET' ,
},
});
// Automatic breadcrumbs from integrations
Sentry . init ({
dsn: '__DSN__' ,
integrations: [
Sentry . breadcrumbsIntegration ({
console: true , // Console logs
dom: true , // DOM events
fetch: true , // Fetch requests
history: true , // Navigation
sentry: true , // Sentry API calls
xhr: true , // XMLHttpRequest
}),
],
});
Scope Management
Isolation Scope
Use isolation scopes for request-level context in Node.js:
import * as Sentry from '@sentry/node' ;
app . use (( req , res , next ) => {
Sentry . withIsolationScope (( scope ) => {
scope . setTag ( 'request_id' , req . id );
scope . setUser ({ id: req . userId });
next ();
});
});
Current Scope
Modify the current scope temporarily:
// Set data on current scope
Sentry . getCurrentScope (). setTag ( 'feature' , 'new-checkout' );
// Isolate scope for a specific operation
Sentry . withScope (( scope ) => {
scope . setLevel ( 'warning' );
scope . setTag ( 'transaction_type' , 'refund' );
Sentry . captureMessage ( 'Refund processed' );
});
// Scope changes are discarded after the callback
Error Boundaries (React)
Use error boundaries to catch React component errors:
import * as Sentry from '@sentry/react' ;
function App () {
return (
< Sentry.ErrorBoundary
fallback = { < ErrorFallback /> }
showDialog
onError = { ( error , componentStack , eventId ) => {
console . log ( 'Error caught by boundary:' , error );
} }
>
< YourApp />
</ Sentry.ErrorBoundary >
);
}
function ErrorFallback ({ error , componentStack , resetError }) {
return (
< div >
< h1 > Something went wrong </ h1 >
< button onClick = { resetError } > Try again </ button >
</ div >
);
}
Customizing Error Boundaries
import { ErrorBoundary } from '@sentry/react' ;
function MyErrorBoundary ({ children }) {
return (
< ErrorBoundary
beforeCapture = { ( scope , error , errorInfo ) => {
scope . setTag ( 'error_boundary' , 'MyErrorBoundary' );
scope . setContext ( 'component_stack' , {
stack: errorInfo . componentStack ,
});
} }
onError = { ( error , componentStack , eventId ) => {
// Log to analytics
analytics . track ( 'error_boundary_caught' , {
eventId ,
error: error . message ,
});
} }
onReset = { () => {
// Reset app state
window . location . href = '/' ;
} }
fallback = { ({ error , resetError }) => (
< div >
< h2 > Oops! Something went wrong </ h2 >
< pre > { error . message } </ pre >
< button onClick = { resetError } > Reset </ button >
</ div >
) }
>
{ children }
</ ErrorBoundary >
);
}
Async Error Handling
Promises
// Unhandled rejections are caught automatically
fetch ( '/api/data' )
. then ( response => response . json ())
. then ( data => processData ( data ));
// If any step throws, Sentry captures it
// Manual capture in catch
fetch ( '/api/data' )
. then ( response => response . json ())
. catch ( error => {
Sentry . captureException ( error );
throw error ; // Rethrow if needed
});
Async/Await
async function fetchUserData ( userId ) {
try {
const response = await fetch ( `/api/users/ ${ userId } ` );
const data = await response . json ();
return data ;
} catch ( error ) {
Sentry . captureException ( error );
// Add context
Sentry . setContext ( 'api_call' , {
userId ,
endpoint: '/api/users' ,
});
throw error ;
}
}
Node.js Error Handling
Express Error Handler
import express from 'express' ;
import * as Sentry from '@sentry/node' ;
const app = express ();
// Add Sentry error handler AFTER all controllers and BEFORE other error handlers
app . use ( Sentry . setupExpressErrorHandler ( app ));
// Your error handler
app . use (( err , req , res , next ) => {
res . status ( 500 ). send ( 'Internal Server Error' );
});
Unhandled Rejections
import * as Sentry from '@sentry/node' ;
Sentry . init ({
dsn: '__DSN__' ,
integrations: [
// Enabled by default
Sentry . onUncaughtExceptionIntegration ({
onFatalError : ( error ) => {
console . error ( 'Fatal error:' , error );
process . exit ( 1 );
},
}),
Sentry . onUnhandledRejectionIntegration ({
mode: 'warn' , // 'warn' or 'strict'
}),
],
});
Enriching Error Data
Stack Traces
import * as Sentry from '@sentry/browser' ;
Sentry . init ({
dsn: '__DSN__' ,
attachStacktrace: true , // Attach stack traces to messages
});
Source Maps
Ensure source maps are uploaded for readable stack traces:
// next.config.js (Next.js)
const { withSentryConfig } = require ( '@sentry/nextjs' );
module . exports = withSentryConfig (
{
// Next.js config
},
{
// Sentry webpack plugin config
silent: true ,
org: 'your-org' ,
project: 'your-project' ,
}
);
Context Lines Integration
import { contextLinesIntegration } from '@sentry/browser' ;
Sentry . init ({
dsn: '__DSN__' ,
integrations: [
contextLinesIntegration (), // Shows source code around error
],
});
Handling Specific Error Types
Network Errors
async function fetchWithErrorHandling ( url ) {
try {
const response = await fetch ( url );
if ( ! response . ok ) {
const error = new Error ( `HTTP ${ response . status } ` );
Sentry . withScope (( scope ) => {
scope . setTag ( 'http_status' , response . status );
scope . setContext ( 'request' , {
url ,
method: 'GET' ,
status: response . status ,
});
Sentry . captureException ( error );
});
throw error ;
}
return response . json ();
} catch ( error ) {
if ( error . name === 'TypeError' ) {
// Network error
Sentry . withScope (( scope ) => {
scope . setTag ( 'error_type' , 'network' );
scope . setLevel ( 'warning' );
Sentry . captureException ( error );
});
}
throw error ;
}
}
Validation Errors
function validateUserInput ( data ) {
const errors = [];
if ( ! data . email ) {
errors . push ( 'Email required' );
}
if ( errors . length > 0 ) {
const error = new Error ( 'Validation failed' );
Sentry . withScope (( scope ) => {
scope . setLevel ( 'info' ); // Validation errors aren't critical
scope . setContext ( 'validation' , {
errors ,
data: { ... data , password: '[Filtered]' },
});
Sentry . captureException ( error );
});
throw error ;
}
}
Best Practices
Always add context before capturing
Context helps debug issues faster: // ❌ Bad
Sentry . captureException ( error );
// ✅ Good
Sentry . withScope (( scope ) => {
scope . setTag ( 'feature' , 'checkout' );
scope . setContext ( 'cart' , { items: 3 , total: 99.99 });
Sentry . captureException ( error );
});
Use error levels appropriately
Set appropriate severity levels: // Fatal: app crashes
scope . setLevel ( 'fatal' );
// Error: exceptions
scope . setLevel ( 'error' );
// Warning: deprecated API usage
scope . setLevel ( 'warning' );
// Info: user actions
scope . setLevel ( 'info' );
// Debug: detailed logging
scope . setLevel ( 'debug' );
Don't swallow errors silently
Always log or capture errors: // ❌ Bad
try {
riskyOperation ();
} catch ( error ) {
// Silent failure
}
// ✅ Good
try {
riskyOperation ();
} catch ( error ) {
Sentry . captureException ( error );
// Handle gracefully
showErrorMessage ();
}
Use custom fingerprints for known errors
Group related errors together: try {
await paymentGateway . process ();
} catch ( error ) {
Sentry . withScope (( scope ) => {
if ( error . code === 'INSUFFICIENT_FUNDS' ) {
scope . setFingerprint ([ 'payment' , 'insufficient-funds' ]);
}
Sentry . captureException ( error );
});
}
Monitor error rates, not just errors
Use Sentry’s alerts to monitor error rate increases, which can indicate new issues.
Complete Example
import * as Sentry from '@sentry/browser' ;
Sentry . init ({
dsn: '__DSN__' ,
environment: 'production' ,
attachStacktrace: true ,
integrations: [
Sentry . breadcrumbsIntegration (),
Sentry . globalHandlersIntegration (),
Sentry . contextLinesIntegration (),
],
beforeSend ( event , hint ) {
// Filter out sensitive data
if ( event . request ?. headers ) {
delete event . request . headers [ 'Authorization' ];
}
return event ;
},
});
// Set user context
function setUserContext ( user ) {
Sentry . setUser ({
id: user . id ,
username: user . username ,
email: user . email ,
});
}
// API call with error handling
async function apiCall ( endpoint , options = {}) {
try {
const response = await fetch ( endpoint , options );
if ( ! response . ok ) {
throw new Error ( `HTTP ${ response . status } : ${ response . statusText } ` );
}
return await response . json ();
} catch ( error ) {
Sentry . withScope (( scope ) => {
scope . setTag ( 'api_endpoint' , endpoint );
scope . setContext ( 'request' , {
endpoint ,
method: options . method || 'GET' ,
status: error . status ,
});
if ( error . status >= 500 ) {
scope . setLevel ( 'error' );
} else if ( error . status >= 400 ) {
scope . setLevel ( 'warning' );
}
Sentry . captureException ( error );
});
throw error ;
}
}
export { apiCall , setUserContext };
Next Steps
Performance Monitoring Monitor application performance
Source Maps Configure source maps for readable errors