Skip to main content

Reactor Error Handling

Errors that occur when invoking a reactor will be formatted according to the standard Basis Theory Error Response. The contents of each error will vary based on the failure scenario, and there are several standard error codes that are possible that can be used to determine the cause of the error.

node-bt Error Handling

The @basis-theory/basistheory-reactor-formulas-sdk-js npm package can be referenced within your reactor code to better control the status codes and messages included on reactor errors.

Error Types

This package includes several error types that can be thrown from your code, including:

TypeStatus CodeDetail
AuthenticationError401Authentication Failed
AuthorizationError403Forbidden
BadRequestError400Bad Request
InvalidPaymentMethodError402Invalid Payment Method
RateLimitError429Rate Limit Exceeded
ReactorRuntimeError500Reactor Runtime Error
ServiceUnavailableError503Service Unavailable Error
CustomHttpResponseErroranyN/A

If your reactor code throws any of these standard error types, with the exception of CustomHttpResponseError, they will be caught and translated into a standard Basis Theory error response with the corresponding status code and detail specified in the table above.

If a CustomHttpResponseError is thrown from a reactor, the status code, headers, and body defined by the error will be used to construct a custom API response.

Error Constructors

Each of the error types, excluding CustomHttpResponseError, accepts a constructor argument that will be returned within the errors block. The constructor argument for each type supports several error formats, for example:


// Throwing an error constructed with a string error message:
const { ReactorRuntimeError } = require('@basis-theory/basis-theory-reactor-formulas-sdk-js');
throw new ReactorRuntimeError('My error message');

// Would result in the following error response:
{
"status": 500,
"detail": "Reactor Runtime Error",
"errors": {
"error": ["My error message"]
}
}

Standard Error Responses

Standard error types (excluding CustomHttpResponseError) are caught and translated into a standard Basis Theory error response with the corresponding status code and detail.

For example, throwing an AuthenticationError:

const { AuthenticationError } = require('@basis-theory/basis-theory-reactor-formulas-sdk-js');

module.exports = async function (req) {
if (!req.args.apiKey) {
throw new AuthenticationError('API key is required');
}

// ...
};

would result in the following error response with a 401 status code:

{
"status": 401,
"detail": "Authentication Failed",
"errors": {
"error": ["API key is required"]
}
}

Custom Error Responses

To have complete control over the response body that is returned, including custom status codes, custom headers, or to have complete control over the response body, a CustomHttpResponseError can be thrown from a reactor.

For example, throwing the following error:

const { CustomHttpResponseError } = require('@basis-theory/basis-theory-reactor-formulas-sdk-js');
throw new CustomHttpResponseError({
status: 400,
headers: {
"Custom-Response-Header-1": "custom-value-1",
"Custom-Response-Header-2": "custom-value-2"
},
body: {
"custom_property": "value",
"custom_object": {
"custom_nested_property": "value"
}
}
});

results in an API response with status code 400 having the headers:

Custom-Response-Header-1: custom-value-1
Custom-Response-Header-2: custom-value-2

and the response body:

{
"custom_property": "value",
"custom_object": {
"custom_nested_property": "value"
}
}

Uncaught Errors

Any other errors raised from reactor code will result in a ReactorRuntimeError containing the original error object and the response will have status code 500.

node22 Error Handling

The node22 runtime gives you full control over error responses through the return value. Return an appropriate statusCode and error details in the body:

Standard Error Responses

Return a response object with statusCode and body to handle errors. The response format is exactly what you specify. There is no automatic wrapping.

For example:

module.exports = async function (event) {
const { req, logger } = event;

if (!req.card_number) {
logger.warn("Missing required field: card_number");
return {
res: {
body: { error: "card_number is required" },
statusCode: 400
}
};
}

// ...
};

This results in an error response with status code 400:

{
"error": "card_number is required"
}

Custom Error Responses

In node22, you always have full control over error responses. Simply return the desired statusCode, headers, and body in your response object. There is no wrapper or special error type needed. The response you return is sent directly to the client.

For example:

module.exports = async function (event) {
return {
res: {
statusCode: 400,
headers: {
"Custom-Response-Header-1": "custom-value-1",
"Custom-Response-Header-2": "custom-value-2"
},
body: {
myCustomError: "My custom error message"
}
}
};
};

This results in an API response with status code 400, having the specified headers and response body:

{
"myCustomError": "My custom error message"
}

Uncaught Errors

If an uncaught error is thrown from your node22 reactor code (e.g., a runtime exception that isn't caught), it will result in a 500 status code with a generic error response. To provide better error handling, wrap your code in try/catch blocks and return appropriate error responses:

module.exports = async function (event) {
const { req, logger } = event;

try {
// Your processing logic here
if (!req.card_number) {
return {
res: {
body: { error: "card_number is required" },
statusCode: 400
}
};
}

return {
res: {
body: { success: true },
statusCode: 200
}
};
} catch (error) {
logger.error("Processing failed", { error: error.message });
return {
res: {
body: { error: "Internal error" },
statusCode: 500
}
};
}
};

Best Practices

When integrating with an external API within a reactor, we strongly recommend handling all vendor-specific errors and translating them into appropriate error responses.

Some external APIs express error scenarios via HTTP status codes, while others opt to return a generic status code such as 200 OK while expressing the error condition within the response body. Each API is unique and error handling may also look very different when using an SDK vs making a direct HTTP call with a generic HTTP client. For these reasons, error handling logic needs to be customized for each external API call made from within your reactor code.

In order to make it easier to debug errors returned by a reactor, we strongly encourage you to handle all potential errors and to craft your reactor responses such that you have sufficient information available in your system to react appropriately to each error scenario.

Examples

module.exports = async function (req) {
const stripe = require('stripe')(req.configuration.STRIPE_PRIVATE_API_KEY);
const {
AuthenticationError,
BadRequestError,
InvalidPaymentMethodError,
RateLimitError,
ReactorRuntimeError,
} = require('@basis-theory/basis-theory-reactor-formulas-sdk-js');

try {
const token = await stripe.tokens.create({
// redacted for simplicity
});

return {
raw: token,
};
} catch (err) {
// the stripe sdk throws an error containing a type value
// here we translate from stripe-specific error types into Basis Theory errors
switch (err.type) {
case 'StripeCardError':
throw new InvalidPaymentMethodError();
case 'StripeRateLimitError':
throw new RateLimitError();
case 'StripeAuthenticationError':
throw new AuthenticationError();
case 'StripeInvalidRequestError':
throw new BadRequestError();
default:
throw new ReactorRuntimeError(err);
}
}
};