Skip to content

Error Handling

VentureKit provides structured error classes that automatically map to HTTP status codes and JSON error responses. Throw any VentureError subclass in your handler and the error boundary middleware handles the rest.

ClassStatusCodeUse Case
BadRequestError400BAD_REQUESTInvalid input
UnauthorizedError401UNAUTHORIZEDMissing/invalid authentication
ForbiddenError403FORBIDDENAuthenticated but not allowed
NotFoundError404NOT_FOUNDResource doesn’t exist
ConflictError409CONFLICTState conflict or duplicate
ValidationError422VALIDATION_ERRORField validation failed
RateLimitError429RATE_LIMITEDToo many requests
InternalError500INTERNAL_ERRORUnexpected server error
ServiceUnavailableError503SERVICE_UNAVAILABLEDependency failure
import { handler, NotFoundError, BadRequestError } from '@venturekit/runtime';
export const main = handler(async (body, ctx, logger) => {
if (!body.title) {
throw new BadRequestError('Title is required');
}
const task = await findTask(body.id);
if (!task) {
throw new NotFoundError('Task', body.id);
}
return task;
}, { scopes: ['tasks.read'] });

All errors produce a consistent JSON response:

{
"error": {
"code": "NOT_FOUND",
"message": "Task '123' not found",
"details": {
"resource": "Task",
"id": "123"
}
}
}
import { ValidationError } from '@venturekit/runtime';
throw new ValidationError('Validation failed', {
title: ['Title is required', 'Title must be at least 3 characters'],
email: ['Invalid email format'],
});

Response:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": {
"fields": {
"title": ["Title is required", "Title must be at least 3 characters"],
"email": ["Invalid email format"]
}
}
}
}
import { RateLimitError } from '@venturekit/runtime';
throw new RateLimitError(60); // Retry after 60 seconds
import { ServiceUnavailableError } from '@venturekit/runtime';
throw new ServiceUnavailableError('Database');
// → "Database is currently unavailable" (503)

Extend VentureError for domain-specific errors:

import { VentureError } from '@venturekit/runtime';
class PaymentRequiredError extends VentureError {
constructor(message: string = 'Payment required') {
super('PAYMENT_REQUIRED', message, 402);
this.name = 'PaymentRequiredError';
}
}

Check if an error is a VentureKit error:

import { isVentureError } from '@venturekit/runtime';
try {
await doSomething();
} catch (error) {
if (isVentureError(error)) {
logger.warn('Known error', { code: error.code, status: error.statusCode });
} else {
logger.error('Unexpected error', { error });
}
}

The handler() function automatically:

  1. Wraps your code in an error boundary middleware
  2. Catches any thrown error
  3. If it’s a VentureError, returns the appropriate HTTP status and JSON body
  4. If it’s an unknown error, returns 500 Internal Server Error with a safe message
  5. Logs the error with the request context