Error Handling
All errors can be imported from the @shipengine/connect-runtime
package and are thrown like normal JavaScript errors. These predefined errors are what we use to map to the appropriate http status codes in the response.
Warning
Avoid intentionally throwing generic JavaScript errors. These errors will be represented as an issue with your integration, and can cause your integration to be turned off from public consumption.
throw new Error('This is a bad idea'); // Don't do this.
Bad Request Error
A bad request error can be used to let consumers of your integration know that the request they sent your integration was either missing information, or was not a valid request. This lets the customer know what they can update to make a successful request in the future.
const { BadRequestError } = require('@shipengine/connect-runtime');
...
throw new BadRequestError('Destination zip code is a required property.');
You can also choose to throw an error with additional information for logging purposes.
const { BadRequestError, ErrorCode } = require('@shipengine/connect-runtime');
const { apiCall } = require('@example/api');
...
const result = apiCall(...);
const { status, message } = result.body;
if(status === 'bad-request') {
const { errors } = result.body;
const detailedErrors = errors.map(error => {
return {
externalErrorCode: error.CODE,
message: error.WARNING,
externalHttpStatusCode: result.statusCode,
externalContext: error,
errorCode: ErrorCode.ExternalClientError
};
})
throw new BadRequestError(message, detailedErrors);
}
The definition for the BadRequestError is as follows
export interface ErrorDetail {
/** The error code associated with the third parties system.
* Useful for support to coordinate with third parties. */
externalErrorCode?: string;
/** The human readable message associated with this error */
message?: string;
/** The external http status code if applicable */
externalHttpStatusCode?: number;
/** Any object that might be useful in logs associated with this error */
externalContext?: any;
/** A standardized error code */
errorCode?: ErrorCode;
/** Path to the field that caused the error, if any **/
fieldName?: string;
}
export declare class BadRequestError extends BaseError {
constructor(message: string, details?: ErrorDetail[] | ErrorDetail);
}
Validation Error
A validation error can be used to let consumers of your integration know that the request they sent your integration had invalid data in one or more fields. This lets the customer know what they can update to make a successful request in the future. This is a superset of the Bad Request Error and is meant to make returning validation errors easier.
You are encouraged to validate input for known requirements to avoid making third party API calls that will fail. If the validation must be performed by the third party API, a best effort should be made to interpret the API error response so that a ValidationError
with fieldName
(s) can be thrown.
const { ValidationError } = require('@shipengine/connect-runtime');
...
throw new ValidationError('ship_to.postal_code', 'Destination postal code is a required property.');
You can also throw an error with multiple fields:
const { ValidationError, ErrorCode } = require('@shipengine/connect-runtime');
...
throw new ValidationError([
{ fieldName: 'ship_to.postal_code', reason: 'Destination postal code is a required property.' },
{ fieldName: 'packages[0].weight_details.weight_in_ounces', reason: 'Weight must be less than 300 ounces.' },
]);
The definition for the ValidationError is as follows
export declare class ValidationError extends BaseError {
constructor(fields: { fieldName: string; reason: string }[]);
constructor(fieldName: string, reason: string);
}
Unauthorized Error
An unauthorized error lets users know that their credentials have either expired or are incorrect. This can help our platform handle token refresh scenarios for oauth, as well as our support trouble shoot issues with customers credentials.
const { UnauthorizedError } = require('@shipengine/connect-runtime');
...
throw new UnauthorizedError('The access token provided has expired');
You can also choose to throw an error with additional information for logging purposes.
const { UnauthorizedError, ErrorCode } = require('@shipengine/connect-runtime');
const { apiCall } = require('@example/api');
...
const result = apiCall(...);
const { status, message } = result.body;
if(result.statusCode === 401) {
const { errors } = result.body;
const detailedErrors = errors.map(error => {
return {
externalErrorCode: error.CODE,
message: error.WARNING,
externalHttpStatusCode: result.statusCode,
externalContext: error,
errorCode: ErrorCode.ExternalClientError
};
})
throw new UnauthorizedError(message, detailedErrors);
}
The definition for the UnauthorizedError is as follows
export interface ErrorDetail {
/** The error code associated with the third parties system.
* Useful for support to coordinate with third parties. */
externalErrorCode?: string;
/** The human readable message associated with this error */
message?: string;
/** The external http status code if applicable */
externalHttpStatusCode?: number;
/** Any object that might be useful in logs associated with this error */
externalContext?: any;
/** A standardized error code */
errorCode?: ErrorCode;
}
export declare class UnauthorizedError extends BaseError {
constructor(message: string, details?: ErrorDetail[] | ErrorDetail);
}
Rate Limit Error
A rate limit error lets our platform know that the external api is requesting that we slow down on making requests. This can often times be found in the form of an http status code 429 - Too Many Requests
.
const { RateLimitError } = require('@shipengine/connect-runtime');
...
throw new RateLimitError('The third party requests you slow your roll.');
You can also choose to throw an error with additional details to help the platform determine when an appropriate time to start calling again would be.
const { RateLimitError } = require('@shipengine/connect-runtime');
const { apiCall } = require('@example/api');
...
const result = apiCall(...);
const { seconds_until_retry } = result.body;
if(result.statusCode === 429) {
const { errors } = result.body;
const rateLimitDetails = {
retryAfterSeconds: seconds_until_retry,
throttlingContext: result.body,
}
throw new RateLimitError(message, rateLimitDetails);
The definition for the RateLimitError is as follows
export interface RateLimitDetails {
retryAfterSeconds?: number;
retryAfterTime?: string;
throttlingContext?: any;
}
export declare class RateLimitError extends BaseError {
constructor(message: string, details?: RateLimitDetails);
}
External Server Error
An external server error lets consumers of your integration know that the third party is having a rough time. Their servers or networks might be experiencing issues, this should be thrown whenever the third party returns an http status code >= 500.
const { ExternalServerError } = require('@shipengine/connect-runtime');
...
throw new ExternalServerError('The third parties servers are unavailable.');
You can also choose to throw an error with additional details to help the platform determine when an appropriate time to start calling again would be.
const { ExternalServerError } = require('@shipengine/connect-runtime');
const { apiCall } = require('@example/api');
...
const result = apiCall(...);
if(result.statusCode >= 500) {
throw new ExternalServerError('The third parties api is not accessible at the moment', {
externalHttpStatusCode: result.statusCode,
externalContext: result.body
});
The definition for the ExternalServerError is as follows
export interface ErrorDetail {
/** The error code associated with the third parties system.
* Useful for support to coordinate with third parties. */
externalErrorCode?: string;
/** The human readable message associated with this error */
message?: string;
/** The external http status code if applicable */
externalHttpStatusCode?: number;
/** Any object that might be useful in logs associated with this error */
externalContext?: any;
/** A standardized error code */
errorCode?: ErrorCode;
}
export declare class ExternalServerError extends BaseError {
constructor(message: string, details?: ErrorDetail[] | ErrorDetail);
}