Capture exceptions manually or automatically with full stack traces, source context, and rate limiting.
Capture an exception manually and queue it for sending.
/**
* Capture an error exception as an event
* @param error - The error to capture
* @param distinctId - Optional user distinct ID
* @param additionalProperties - Optional additional properties to include
*/
captureException(
error: unknown,
distinctId?: string,
additionalProperties?: Record<string | number, any>
): void;Usage Examples:
import { PostHog } from 'posthog-node';
const client = new PostHog('phc_your_api_key');
// Basic exception capture
try {
riskyOperation();
} catch (error) {
client.captureException(error, 'user_123');
}
// With additional properties
try {
await apiCall();
} catch (error) {
client.captureException(error, 'user_123', {
endpoint: '/api/users',
method: 'POST',
status_code: 500
});
}
// Without user ID (uses personless processing)
try {
backgroundJob();
} catch (error) {
client.captureException(error);
}
// With context properties
try {
processPayment(orderId);
} catch (error) {
client.captureException(error, 'user_123', {
order_id: orderId,
payment_method: 'credit_card',
amount: 99.99
});
}Capture an exception and send it immediately without queuing. Use for serverless environments or critical errors.
/**
* Capture an error exception as an event immediately (synchronously)
* @param error - The error to capture
* @param distinctId - Optional user distinct ID
* @param additionalProperties - Optional additional properties to include
* @returns Promise that resolves when the error is captured
*/
async captureExceptionImmediate(
error: unknown,
distinctId?: string,
additionalProperties?: Record<string | number, any>
): Promise<void>;Usage Examples:
// Immediate capture in serverless function
export async function handler(event) {
try {
return await processEvent(event);
} catch (error) {
await client.captureExceptionImmediate(error, event.userId);
throw error;
}
}
// With additional context
try {
await criticalOperation();
} catch (error) {
await client.captureExceptionImmediate(error, 'user_123', {
operation: 'critical_operation',
severity: 'high'
});
throw error;
}
// In API endpoint
app.post('/api/action', async (req, res) => {
try {
const result = await performAction(req.body);
res.json({ success: true, result });
} catch (error) {
await client.captureExceptionImmediate(error, req.user?.id, {
endpoint: '/api/action',
body: req.body
});
res.status(500).json({ error: 'Internal error' });
}
});Enable automatic capture of uncaught exceptions and unhandled promise rejections.
import { PostHog } from 'posthog-node';
// Enable automatic exception capture
const client = new PostHog('phc_your_api_key', {
enableExceptionAutocapture: true
});When enableExceptionAutocapture is enabled:
Usage Examples:
// Enable autocapture
const client = new PostHog('phc_your_api_key', {
enableExceptionAutocapture: true
});
// Uncaught exceptions are automatically captured
function riskyFunction() {
throw new Error('This will be automatically captured');
}
// Unhandled rejections are automatically captured
async function asyncRiskyFunction() {
throw new Error('This will be automatically captured');
}
// The application continues running after non-fatal errors
setTimeout(() => {
riskyFunction();
}, 1000);
// Fatal errors trigger shutdown
process.on('uncaughtException', (error) => {
// PostHog captures this automatically and then exits
});import express from 'express';
import { PostHog } from 'posthog-node';
const app = express();
const client = new PostHog('phc_your_api_key');
// Route-level error handling
app.get('/api/users/:id', async (req, res) => {
try {
const user = await getUser(req.params.id);
res.json(user);
} catch (error) {
client.captureException(error, req.user?.id, {
endpoint: '/api/users/:id',
user_id: req.params.id
});
res.status(500).json({ error: 'Failed to fetch user' });
}
});
// Application-level error middleware
app.use((error, req, res, next) => {
client.captureException(error, req.user?.id, {
endpoint: req.path,
method: req.method,
status: error.status || 500
});
res.status(error.status || 500).json({
error: error.message || 'Internal server error'
});
});// Basic async error handling
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
client.captureException(error, 'user_123', {
operation: 'fetch_data',
url: 'https://api.example.com/data'
});
throw error;
}
}
// With retry logic
async function fetchDataWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
if (i === retries - 1) {
client.captureException(error, undefined, {
operation: 'fetch_data_with_retry',
url: url,
attempts: retries
});
throw error;
}
}
}
}// Promise chain error handling
fetchUser('user_123')
.then(user => processUser(user))
.then(result => saveResult(result))
.catch(error => {
client.captureException(error, 'user_123', {
operation: 'user_processing_pipeline'
});
});
// Promise.all error handling
Promise.all([
fetchUsers(),
fetchOrders(),
fetchProducts()
])
.then(([users, orders, products]) => {
return combineData(users, orders, products);
})
.catch(error => {
client.captureException(error, undefined, {
operation: 'parallel_data_fetch'
});
});// MongoDB error handling
async function createUser(userData) {
try {
const user = await db.users.insertOne(userData);
return user;
} catch (error) {
client.captureException(error, userData.id, {
operation: 'create_user',
database: 'mongodb',
collection: 'users',
duplicate_key: error.code === 11000
});
throw error;
}
}
// PostgreSQL error handling
async function queryUsers(filters) {
try {
const result = await pool.query('SELECT * FROM users WHERE ...', filters);
return result.rows;
} catch (error) {
client.captureException(error, undefined, {
operation: 'query_users',
database: 'postgresql',
query: 'SELECT * FROM users WHERE ...',
error_code: error.code
});
throw error;
}
}// Worker queue error handling
async function processJob(job) {
try {
await job.process();
await job.complete();
} catch (error) {
await client.captureExceptionImmediate(error, job.userId, {
job_id: job.id,
job_type: job.type,
attempt: job.attemptsMade,
max_attempts: job.maxAttempts
});
await job.fail(error);
}
}
// Cron job error handling
cron.schedule('0 * * * *', async () => {
try {
await runHourlyTask();
} catch (error) {
client.captureException(error, undefined, {
job_type: 'cron',
schedule: '0 * * * *',
task: 'hourly_task'
});
}
});// Stripe API error handling
async function createCharge(amount, currency, customer) {
try {
const charge = await stripe.charges.create({
amount,
currency,
customer
});
return charge;
} catch (error) {
client.captureException(error, customer, {
operation: 'stripe_charge',
amount: amount,
currency: currency,
stripe_error_type: error.type,
stripe_error_code: error.code
});
throw error;
}
}
// SendGrid API error handling
async function sendEmail(to, subject, body) {
try {
await sgMail.send({ to, subject, html: body });
} catch (error) {
client.captureException(error, to, {
operation: 'send_email',
email_to: to,
subject: subject,
response_code: error.code
});
throw error;
}
}// AWS Lambda
export async function handler(event, context) {
try {
const result = await processEvent(event);
return {
statusCode: 200,
body: JSON.stringify(result)
};
} catch (error) {
await client.captureExceptionImmediate(error, event.userId, {
function_name: context.functionName,
request_id: context.awsRequestId,
event_type: event.type
});
return {
statusCode: 500,
body: JSON.stringify({ error: 'Internal error' })
};
}
}
// Vercel Serverless Function
export default async function handler(req, res) {
try {
const result = await processRequest(req);
res.json(result);
} catch (error) {
await client.captureExceptionImmediate(error, req.user?.id, {
url: req.url,
method: req.method
});
res.status(500).json({ error: 'Internal error' });
}
}// Custom error classes
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
class DatabaseError extends Error {
constructor(message, query) {
super(message);
this.name = 'DatabaseError';
this.query = query;
}
}
// Handling custom errors
try {
validateInput(data);
} catch (error) {
if (error instanceof ValidationError) {
client.captureException(error, 'user_123', {
error_type: 'validation',
field: error.field,
severity: 'low'
});
} else if (error instanceof DatabaseError) {
client.captureException(error, 'user_123', {
error_type: 'database',
query: error.query,
severity: 'high'
});
} else {
client.captureException(error, 'user_123', {
error_type: 'unknown',
severity: 'medium'
});
}
throw error;
}PostHog automatically extracts the following properties from exceptions:
$exception_message - Error message$exception_type - Error type/class name$exception_level - Severity level (error, warning, etc.)$exception_list - Array of exception details with stack traces// Good: Rich context
try {
await processOrder(orderId);
} catch (error) {
client.captureException(error, userId, {
order_id: orderId,
user_id: userId,
payment_method: 'stripe',
amount: 99.99,
step: 'charge_card'
});
}
// Avoid: No context
try {
await processOrder(orderId);
} catch (error) {
client.captureException(error);
}// Good: With user ID
try {
await userOperation();
} catch (error) {
client.captureException(error, req.user.id);
}
// Acceptable: Without user ID for system errors
try {
await systemOperation();
} catch (error) {
client.captureException(error); // Uses personless processing
}// Good: Capture and re-throw
try {
await criticalOperation();
} catch (error) {
client.captureException(error, userId);
throw error; // Let caller handle it
}
// Avoid: Swallowing errors
try {
await criticalOperation();
} catch (error) {
client.captureException(error, userId);
// Error is lost!
}// Good: Immediate capture in serverless
export async function handler(event) {
try {
return await process(event);
} catch (error) {
await client.captureExceptionImmediate(error, event.userId);
throw error;
}
}
// Avoid: Queued capture in serverless (may be lost)
export async function handler(event) {
try {
return await process(event);
} catch (error) {
client.captureException(error, event.userId); // May not send!
throw error;
}
}// Good: Enable for production with proper monitoring
const client = new PostHog('phc_your_api_key', {
enableExceptionAutocapture: true
});
// Good: Disable in development
const client = new PostHog('phc_your_api_key', {
enableExceptionAutocapture: process.env.NODE_ENV === 'production'
});// Categorize errors by severity
try {
await operation();
} catch (error) {
const severity = error instanceof ValidationError ? 'low' :
error instanceof DatabaseError ? 'high' :
'medium';
client.captureException(error, userId, {
severity: severity,
requires_action: severity === 'high'
});
}// Add grouping properties
try {
await apiCall();
} catch (error) {
client.captureException(error, userId, {
error_group: 'external_api',
service: 'stripe',
endpoint: '/v1/charges'
});
}Automatic exception capture includes rate limiting to prevent flooding:
This prevents a single error from dominating your exception tracking while still capturing diverse error types.
When no distinctId is provided, exceptions use personless processing:
$process_person_profile: false// Personless processing (good for system errors)
client.captureException(error); // No distinctId
// User-associated processing (good for user actions)
client.captureException(error, 'user_123');