Utilities for converting errors to JSON-safe format and back, enabling error transmission over networks, consistent logging formats, and error persistence. The serialization system preserves error properties while handling non-serializable elements gracefully.
Serializes an error object to a JSON-friendly form, with optional stack trace inclusion.
/**
* Serializes an error object to a JSON friendly form.
* @param error - The error to serialize
* @param options - Optional serialization options
* @returns Serialized error object safe for JSON transmission
*/
function serializeError(
error: Error,
options?: {
/** Include stack trace in the output (default false) */
includeStack?: boolean;
}
): SerializedError;
type SerializedError = {
/** The name of the exception that was thrown */
name: string;
/** The message of the exception that was thrown */
message: string;
/** A stringified stack trace; may not be present */
stack?: string;
/** A custom code (not necessarily the same as an HTTP response code); may not be present */
code?: string;
/** Additional error properties preserved during serialization */
[key: string]: unknown;
};Usage Examples:
import { serializeError, InputError } from "@backstage/errors";
// Basic serialization without stack trace
const error = new InputError("Invalid user input");
const serialized = serializeError(error);
console.log(JSON.stringify(serialized, null, 2));
// {
// "name": "InputError",
// "message": "Invalid user input"
// }
// Serialization with stack trace for debugging
const serializedWithStack = serializeError(error, { includeStack: true });
console.log(JSON.stringify(serializedWithStack, null, 2));
// {
// "name": "InputError",
// "message": "Invalid user input",
// "stack": "InputError: Invalid user input\n at Object.<anonymous>..."
// }
// Serializing errors with custom properties
const customError = new Error("Database connection failed");
(customError as any).code = 'DB_CONN_FAILED';
(customError as any).retryAfter = 5000;
const serializedCustom = serializeError(customError);
// Preserves custom properties: code, retryAfterDeserializes a serialized error object back to an Error instance.
/**
* Deserializes a serialized error object back to an Error.
* @param data - The serialized error data
* @returns Restored Error instance with preserved properties
*/
function deserializeError<T extends Error = Error>(
data: SerializedError
): T;Usage Examples:
import { deserializeError, serializeError } from "@backstage/errors";
// Round-trip serialization
const originalError = new Error("Something went wrong");
const serialized = serializeError(originalError);
const restored = deserializeError(serialized);
console.log(restored instanceof Error); // true
console.log(restored.name); // "Error"
console.log(restored.message); // "Something went wrong"
// Deserializing from JSON (e.g., from API response)
const errorJson = `{
"name": "NotFoundError",
"message": "User not found",
"code": "USER_404"
}`;
const errorData = JSON.parse(errorJson);
const error = deserializeError(errorData);
console.log(error.name); // "NotFoundError"
console.log((error as any).code); // "USER_404"Converts an error to a human-readable string representation, handling both Error objects and unknown values safely.
/**
* Stringifies an error, including its name and message where available.
* @param error - The error or unknown value to stringify
* @returns Human-readable string representation
*/
function stringifyError(error: unknown): string;Usage Examples:
import { stringifyError, AuthenticationError } from "@backstage/errors";
// Standard Error objects
const authError = new AuthenticationError("Token expired");
console.log(stringifyError(authError));
// "AuthenticationError: Token expired"
// Custom Error with toString override
class CustomError extends Error {
toString() {
return `Custom: ${this.message}`;
}
}
const custom = new CustomError("Something failed");
console.log(stringifyError(custom));
// "Custom: Something failed"
// Non-error values
console.log(stringifyError("just a string"));
// "unknown error 'just a string'"
console.log(stringifyError(null));
// "unknown error 'null'"
console.log(stringifyError({ message: "not an error" }));
// "unknown error '[object Object]'"
// Error-like objects
const errorLike = { name: "MyError", message: "Failed operation" };
console.log(stringifyError(errorLike));
// "MyError: Failed operation"Attempts to construct an ErrorResponseBody from a failed server request, with forgiving parsing that creates synthetic bodies when needed.
/**
* Attempts to construct an ErrorResponseBody out of a failed server request.
* Assumes that the response has already been checked to be not ok. This
* function consumes the body of the response, and assumes that it hasn't
* been consumed before.
*
* The code is forgiving, and constructs a useful synthetic body as best it can
* if the response body wasn't on the expected form.
*
* @param response - The response of a failed request
* @returns Parsed or synthetic ErrorResponseBody
*/
async function parseErrorResponseBody(
response: ConsumedResponse & { text(): Promise<string> }
): Promise<ErrorResponseBody>;
type ErrorResponseBody = {
/** Details of the error that was caught */
error: SerializedError;
/** Details about the incoming request */
request?: {
/** The HTTP method of the request */
method: string;
/** The URL of the request (excluding protocol and host/port) */
url: string;
};
/** Details about the response */
response: {
/** The numeric HTTP status code that was returned */
statusCode: number;
};
};Usage Examples:
import { parseErrorResponseBody, ErrorResponseBody } from "@backstage/errors";
async function handleFailedRequest(response: Response) {
// Ensure response is not ok before parsing
if (response.ok) {
throw new Error("Response is ok, cannot parse as error");
}
const errorBody = await parseErrorResponseBody(response);
// Standard error response format
if (errorBody.error && errorBody.response) {
console.log(`Server Error: ${errorBody.error.name}`);
console.log(`Message: ${errorBody.error.message}`);
console.log(`Status: ${errorBody.response.statusCode}`);
if (errorBody.request) {
console.log(`Failed Request: ${errorBody.request.method} ${errorBody.request.url}`);
}
}
}
// Usage with fetch
async function apiRequest(url: string) {
const response = await fetch(url);
if (!response.ok) {
const errorBody = await parseErrorResponseBody(response);
// Log structured error information
console.error('API Request Failed:', {
status: response.status,
statusText: response.statusText,
error: errorBody.error,
url: url
});
throw new Error(`API Error: ${errorBody.error.message}`);
}
return response.json();
}import {
serializeError,
stringifyError,
ErrorResponseBody
} from "@backstage/errors";
class ErrorLogger {
static logError(error: unknown, context?: Record<string, unknown>) {
const serialized = serializeError(
error instanceof Error ? error : new Error(String(error)),
{ includeStack: true }
);
const logEntry = {
timestamp: new Date().toISOString(),
error: serialized,
context,
summary: stringifyError(error)
};
console.error(JSON.stringify(logEntry, null, 2));
}
static logApiError(response: Response, errorBody: ErrorResponseBody) {
this.logError(errorBody.error, {
httpStatus: response.status,
httpStatusText: response.statusText,
requestMethod: errorBody.request?.method,
requestUrl: errorBody.request?.url,
responseStatusCode: errorBody.response.statusCode
});
}
}import { serializeError, deserializeError } from "@backstage/errors";
// Client side - sending errors to server
class ErrorReporter {
static async reportError(error: Error, metadata?: Record<string, unknown>) {
const payload = {
error: serializeError(error, { includeStack: true }),
metadata,
timestamp: Date.now(),
userAgent: navigator.userAgent
};
await fetch('/api/errors', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
}
}
// Server side - processing received errors
class ErrorProcessor {
static processErrorReport(payload: any) {
const { error, metadata, timestamp } = payload;
// Restore the error object
const restoredError = deserializeError(error);
// Log with full context
console.error('Client Error Report:', {
error: restoredError,
metadata,
timestamp: new Date(timestamp).toISOString()
});
// Store in database, send to monitoring service, etc.
}
}