or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

error-classes.mdindex.mdserialization.md
tile.json

serialization.mddocs/

Error Serialization

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.

Capabilities

Error Serialization

serializeError

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, retryAfter

deserializeError

Deserializes 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"

stringifyError

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"

HTTP Response Parsing

parseErrorResponseBody

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();
}

Advanced Usage Patterns

Error Logging and Monitoring

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
    });
  }
}

Error Transport Over Networks

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.
  }
}