🚨 Error Handling
Proper error handling is crucial for building robust backend services. The Nodeblocks SDK provides comprehensive error handling patterns that ensure consistent, user-friendly error responses across all services.
🎯 Error Response Format
When nodeBlocksErrorMiddleware() is registered, Nodeblocks service errors use a consistent JSON format:
{
"error": {
"message": "Error message description",
"data": ["Additional error details"]
}
}
Note: Handler and block chains use
orThrowto map domainBlockErrorinstances toNodeblocksError. The error middleware serializes failures in an{ error: { message, data?, stack? } }envelope; clients do not receive top-levelstatusorcodefields from the SDK.
Validation Errors
When request validation fails, additional details are included:
{
"error": {
"message": "Validation Error",
"data": [
"request body must have required property 'name'",
"request body must have required property 'email'",
"request body must NOT have additional properties"
]
}
}
The data array format depends on the schema mode:
- OpenAPI routes prefix body errors with
request body …and parameter errors withpath parameter …orquery parameter …. - Legacy JSON Schema routes use raw AJV messages (for example,
must have required property 'name') without therequest bodyprefix.
📋 Common Error Codes
400 Bad Request
- Validation Error - Invalid request body format or missing required fields
- Failed to create [entity] - Database insert operation failed to return an inserted ID
- Failed to update [entity] - Update operation doesn't modify any data (no changes detected)
- Organization with name … already exists with same owner - Duplicate organization name for the same owner (400, not 409)
401 Unauthorized
- token could not be verified - Token signature or payload verification failed
- wrong credentials provided - Invalid login credentials
- Token fails security check / Token fails security checks - Token fingerprint or security validation failed
- Invalid token - Authenticated payload is not a valid user access token (common in validators such as
checkIdentityType,isSelf,hasOrgRole)
Note: Missing or malformed authorization headers are typically returned as 422 (see below), not 401.
422 Unprocessable Entity
- Missing authorization header - No
Authorizationheader present - Incorrect authorization of token header - Malformed
Authorizationheader value - Incorrect format of
${headerName}header - Header does not match the expected format - Unable to find refresh token - Refresh token missing from cookie or request
- Cookie and bearer did not match - Cookie and bearer token identity mismatch
- Token was not found - Refresh token not found during token refresh
- unable to register "
${email}" - Email already registered (registration conflict; 422, not 409) - One or more members have invalid roles - Invalid roles in organization member upsert
403 Forbidden
- Identity is not authorized to access this resource - Identity lacks required type permissions
- Identity is not authorized to access this organization - Identity lacks a permitted organization role
- Identity is not a member of the organization - Identity is not a member of the target organization
- Identity ID does not match - Authenticated identity does not match the requested resource identity (
isSelf)
404 Not Found
- [Entity] not found - Entity doesn't exist (handler layer; e.g.
Profile not found,Channel not found,Product not found) - [Entity] does not exist - Entity doesn't exist (validator layer; e.g.
Category does not exist,Channel does not exist) - Delete operations - When the target record is missing, delete routes return
[Entity] not found(e.g.Profile not foundon DELETE), notFailed to delete [entity]
409 Conflict
- Email already in use - Duplicate email during registration or email change
- Profile is already followed - Follow relationship already exists
- Organization is already followed - Organization follow relationship already exists
- Product is already liked - Product like relationship already exists
- Email already verified or no changes made - Email confirmation when already verified
- There must be at least one owner remaining in the organization - Organization would have no owners after member change
500 Internal Server Error
- Failed to create [entity] - Database connection issues or unexpected failures during creation
- Failed to get [entity] - Database connection issues or unexpected failures during retrieval
- Failed to find [entities] - Database connection issues, invalid filter syntax, or unexpected failures during listing
- Failed to update [entity] - Database connection issues or unexpected failures during update
- Failed to delete [entity] - Database connection issues or unexpected failures during deletion
🔧 Service-Specific Error Patterns
Note: This section highlights common errors for major services. For complete per-endpoint error tables, see each service's documentation page linked from the Service component overview.
Authentication Service Errors
{
"error": {
"message": "token could not be verified"
}
}
{
"error": {
"message": "wrong credentials provided"
}
}
{
"error": {
"message": "unable to register \"user@example.com\""
}
}
Profile Service Errors
{
"error": {
"message": "Profile not found"
}
}
{
"error": {
"message": "Identity is not authorized to access this resource"
}
}
{
"error": {
"message": "Profile is already followed"
}
}
Organization Service Errors
{
"error": {
"message": "Organization not found"
}
}
{
"error": {
"message": "Failed to create organization"
}
}
{
"error": {
"message": "There must be at least one owner remaining in the organization"
}
}
Product Service Errors
{
"error": {
"message": "Product not found"
}
}
{
"error": {
"message": "Failed to create product"
}
}
{
"error": {
"message": "Product is already liked"
}
}
Category Service Errors
Validator-guarded routes (GET, update, delete, enable, disable) return Category does not exist from the doesCategoryExist validator before the handler runs:
{
"error": {
"message": "Category does not exist"
}
}
Handlers may also emit Category not found if reached without the validator:
{
"error": {
"message": "Category not found"
}
}
{
"error": {
"message": "Failed to create category"
}
}
Attribute Service Errors
{
"error": {
"message": "Attribute group not found"
}
}
{
"error": {
"message": "Attribute not found"
}
}
{
"error": {
"message": "Failed to create attribute group"
}
}
Order Service Errors
{
"error": {
"message": "Order not found"
}
}
{
"error": {
"message": "Failed to create order"
}
}
Chat Service Errors
{
"error": {
"message": "Channel not found"
}
}
The channelExists validator returns Channel does not exist on routes that use it:
{
"error": {
"message": "Channel does not exist"
}
}
GET message by ID uses the block layer and returns Chat message not found. (note the trailing period); update and delete use handlers and return Chat message not found:
{
"error": {
"message": "Chat message not found."
}
}
{
"error": {
"message": "Chat message not found"
}
}
Note: The legacy handler
getChatMessageByIdreturnsMessage not found, but the GET/messages/:messageIdroute uses the blockgetChatMessageByIdBlockinstead. Some block paths also omit the trailing period — match the exact string for the route you are calling.
📐️ Error Handling Best Practices
1. Consistent Error Messages
Use consistent, user-friendly error messages across all services:
// ✅ Good: Clear, actionable error messages
"Profile not found"
"Failed to create organization"
"Validation Error"
// ❌ Avoid: Technical or unclear messages
"Database connection failed"
"Internal server error"
"Something went wrong"
2. Proper HTTP Status Codes
Use appropriate HTTP status codes for different error types:
- 400 - Bad Request (validation errors, missing fields, no-op updates)
- 401 - Unauthorized (authentication failures, invalid tokens)
- 422 - Unprocessable Entity (malformed auth headers, refresh-token state errors, registration conflicts)
- 403 - Forbidden (authorization failures)
- 404 - Not Found (entity doesn't exist)
- 409 - Conflict (duplicate relationships, email already in use)
- 500 - Internal Server Error (database issues, unexpected failures)
3. Validation Error Details
Include specific validation error details in the data array. OpenAPI-validated routes use prefixed messages:
{
"error": {
"message": "Validation Error",
"data": [
"request body must have required property 'name'",
"request body must have required property 'email'",
"request body must NOT have additional properties"
]
}
}
Legacy JSON Schema routes return raw AJV messages in data without the request body prefix.
4. Authentication Error Handling
Use validators to handle authentication errors automatically:
import { primitives, validators } from '@nodeblocks/backend-sdk';
const { withRoute } = primitives;
const { isAuthenticated } = validators;
// Validators automatically throw NodeblocksError with appropriate status codes
export const protectedRoute = withRoute({
method: 'GET',
path: '/protected',
validators: [isAuthenticated()], // Throws 401 or 422 depending on failure mode
handler: protectedHandler,
});
For custom authentication checks in handlers or validators, call context.authenticate — it throws NodeblocksError on failure (422 for missing/malformed headers, 401 for invalid tokens):
import { primitives } from '@nodeblocks/backend-sdk';
const validateAuth: primitives.Validator = async (payload) => {
await payload.context.authenticate?.(payload);
// Throws NodeblocksError (401 or 422) if token is missing or invalid
};
Prefer the built-in isAuthenticated() validator for most routes.
5. Authorization Error Handling
Use validators for authorization checks:
import { primitives, validators } from '@nodeblocks/backend-sdk';
const { withRoute } = primitives;
const { isAuthenticated, checkIdentityType, isSelf, some } = validators;
// Check identity type (e.g., admin only)
export const adminRoute = withRoute({
method: 'GET',
path: '/admin',
validators: [
isAuthenticated(),
checkIdentityType(['admin']), // Throws 403 if not admin
],
handler: adminHandler,
});
// Or allow admin OR self-access to a resource
export const resourceRoute = withRoute({
method: 'GET',
path: '/resource/:id',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
isSelf(['params', 'requestParams', 'id'])
), // Throws the first validator error if all fail (401, 403, or 500 — not always 403)
],
handler: resourceHandler,
});
For custom authorization, prefer built-in validators like checkIdentityType, isSelf, hasOrgRole, and ownsResource. If you must throw manually, use the SDK message wording:
import { primitives } from '@nodeblocks/backend-sdk';
const checkAdminRole: primitives.Validator = async (payload) => {
// Prefer checkIdentityType(['admin']) instead of manual checks.
// Identity type IDs come from configuration (e.g. typeIds.admin = '100'), not literal strings.
throw new primitives.NodeblocksError(
403,
'Identity is not authorized to access this resource',
'checkAdminRole'
);
};
⚙️ Error Middleware Setup
To ensure consistent error handling across your application, use the nodeBlocksErrorMiddleware:
import express from 'express';
import { middlewares, services, drivers } from '@nodeblocks/backend-sdk';
const { nodeBlocksErrorMiddleware } = middlewares;
const { profileService } = services;
const { withMongo } = drivers;
const connectToDatabase = withMongo('mongodb://localhost:27017/?authSource=admin', 'dev', 'user', 'password');
// ProfileServiceDataStore requires all four collections, even if this example
// only exercises basic profile CRUD.
express()
.use(
profileService(
{
...(await connectToDatabase('profiles')),
...(await connectToDatabase('identities')),
...(await connectToDatabase('organizations')),
...(await connectToDatabase('products')),
},
{
authSecrets: {
authEncSecret: 'your-encryption-secret',
authSignSecret: 'your-signing-secret',
},
authMode: 'bearer',
identity: {
typeIds: {
admin: '100',
guest: '000',
regular: '001',
},
},
},
),
)
.use(nodeBlocksErrorMiddleware()) // Must be last
.listen(8089, () => console.log('Server running'));
⚠️ Important: Always add
nodeBlocksErrorMiddleware()after your routes and services to ensure all errors are properly formatted as JSON responses.
🔍 Error Debugging
Development Mode
When NODE_ENV === 'development', the middleware includes the error stack trace:
{
"error": {
"message": "Profile not found",
"stack": "NodeblocksError: Profile not found\n at getProfileById..."
}
}
If the thrown NodeblocksError includes a data property (e.g. validation errors), it is forwarded as-is:
{
"error": {
"message": "Validation Error",
"stack": "NodeblocksError: Validation Error\n at withSchema...",
"data": [
"request body must have required property 'name'"
]
}
}
Production Mode
In production, the middleware returns the error message and any data from the thrown error — it does not add a stack trace. The message is whatever was passed to NodeblocksError:
{
"error": {
"message": "Profile not found"
}
}