🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@vercel/error

Package Overview
Dependencies
Maintainers
4
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vercel/error

A lightweight toolkit for structured, actionable errors for humans and agents

latest
Source
npmnpm
Version
0.0.2
Version published
Weekly downloads
39
-32.76%
Maintainers
4
Weekly downloads
 
Created
Source

@vercel/error

Structured error primitives for humans and agents.

Table of contents

  • Install
  • Entry points
  • Philosophy
  • Quick start
  • Error anatomy
  • Error factories
  • Terminal formatting
  • HTTP error responses
  • Parsing error responses
  • Error utilities
  • Subclassing

Install

pnpm add @vercel/error

Entry points

ImportPurpose
@vercel/errorCore: VercelError, createErrors, guards, extractors, types
@vercel/error/serverServer: errorResponse, wantsAnsi
@vercel/error/clientClient: parseErrorResponse, fromErrorResponse
@vercel/error/formatFormat primitives: frame, hint, fix, link

Philosophy

A good error answers up to five questions:

  • What happened? → message
  • Why did it happen? → reason
  • What could help? → hint
  • How to fix it? → fix
  • Where to learn more? → link

The more questions you answer, the faster the person (or agent) on the other end can resolve the issue.

The rest follows from that:

  • Errors are a unit of communication between services, not crash artifacts. They carry context for humans, agents, and observability tools.
  • Errors should be useful wherever they surface — terminal, log aggregator, HTTP response. The format adapts to the context.
  • Errors are too fundamental to be locked to a framework or runtime.

Quick start

import { VercelError } from '@vercel/error';

throw new VercelError('Database connection pool exhausted', {
  code: 'pool_exhausted',
  scope: 'database',
  statusCode: 503,
  reason: 'All 20 connections are in use and none have been released.',
  hint: 'Consider using pgBouncer for connection pooling.',
  fix: 'Increase max_connections or add pgBouncer.',
  link: 'https://vercel.com/docs/storage/neon#connection-pooling',
});

toString() auto-detects your environment and renders structured output. In a color-capable terminal (TTY or FORCE_COLOR), you get ANSI colors and Unicode tree connectors:

error: VercelError [database:pool_exhausted] Database connection pool exhausted
│
├── All 20 connections are in use and none have been released.
├─▸ hint: Consider using pgBouncer for connection pooling.
├─▸ fix: Increase max_connections or add pgBouncer.
╰─▸ read more: https://vercel.com/docs/storage/neon#connection-pooling

In piped or browser environments, the output falls back to plain indented text. See Terminal formatting for the full detection logic.

For services that create many errors with shared context, see Error factories.

Error anatomy

Every VercelError field falls into one of three groups.

Identity

FieldTypeDescription
codestring (readonly)Machine-readable error code, e.g. "pool_exhausted"
scopestring (readonly)Namespace or service that produced the error, e.g. "database"
statusCodenumberHTTP status code. errorResponse uses this to set the response status (falls back to 500 when unset)

Context

FieldTypeDescription
messagestringWhat happened. The first constructor argument
reasonstringWhy it happened. Explains the root cause
hintstringWhat could help. Advisory information for the developer
fixstringHow to fix it. An actionable remediation step
linkstringWhere to learn more. A URL to relevant documentation
userMessagestringClient-safe message. Set this when message contains internal details you don't want clients to see. errorResponse uses it instead of message when set

Observability

FieldTypeDescription
metadataRecord<string, SerializableValue>Structured context for debugging and logging. Supports arbitrary nesting. Server-side only — excluded from HTTP error responses
attributesRecord<string, string | number | boolean | ...>Flat key-value pairs for OpenTelemetry spans, Sentry tags, and metrics dashboards
requestIdstringCorrelation ID for tracing an error across services
causeunknownStandard Error cause for chaining. Passed through to super() and walkable via getRootCause

Error factories

createErrors defines a scoped error namespace for a service or module. Every error created through the factory gets the scope injected automatically. The report callback defaults to console.error when not provided.

import { createErrors } from '@vercel/error';

const errors = createErrors({
  scope: 'database',
  report: (error) => sentry.captureException(error),
});

The factory returns three methods:

MethodBehavior
create(message, options?)Create and return a VercelError
raise(message, options?)Create and throw (return type: never)
report(message, options?)Create, report via the callback, and return
errors.create('Connection failed', { code: 'conn_failed' });
errors.raise('Timeout', { code: 'timeout' }); // throws
errors.report('Pool exhausted', { code: 'pool_exhausted' }); // reports + returns

Factory-level attributes and metadata

Set attributes and metadata at the factory level. These merge with per-error values automatically, so every error gets baseline context without repetition.

const errors = createErrors({
  scope: 'billing',
  attributes: { 'service.name': 'billing-api' },
  metadata: { region: 'us-east-1' },
});

errors.create('Charge failed', {
  code: 'charge_failed',
  attributes: { 'stripe.error': 'card_declined' },
  metadata: { customerId: 'cus_123' },
});
// attributes: { 'service.name': 'billing-api', 'stripe.error': 'card_declined' }
// metadata: { region: 'us-east-1', customerId: 'cus_123' }

Custom error classes

Pass ErrorClass to create instances of a custom subclass. Type inference flows through, so create, raise, and report all return your subclass type.

import { VercelError, createErrors } from '@vercel/error';
import type { VercelErrorOptions } from '@vercel/error';

class DatabaseError extends VercelError {
  readonly retryable = true;

  constructor(message: string, options?: VercelErrorOptions) {
    super(message, options);
    this.name = 'DatabaseError';
  }
}

const errors = createErrors({
  scope: 'database',
  ErrorClass: DatabaseError,
});

const err = errors.create('Pool exhausted');
err.retryable; // true, fully typed as DatabaseError

Terminal formatting

VercelError.toString() auto-detects your environment and renders structured output with zero configuration.

Auto-detection

The formatter checks the environment in order and uses the first match:

EnvironmentTree structureANSI colorExample
NO_COLOR env var setYesNoNO_COLOR=1 node app.js
FORCE_COLOR env var setYesYesFORCE_COLOR=1 node app.js
TTY terminalYesYesRunning directly in a terminal
Piped, CI, non-TTYNoNonode app.js | cat
BrowserNoNowindow is defined

NO_COLOR respects the no-color.org convention. You still get Unicode tree characters for structure, but no escape codes. FORCE_COLOR forces full ANSI output even without a TTY, which is useful in Docker containers or CI systems that support color.

Color hierarchy

When ANSI color is active, each element has a distinct visual treatment:

ElementColorWeight
error: labelRedBold
Error name and [scope:code] qualifierRedNormal
Error messageRedBold
hint: prefixYellowBold
fix: prefixGreenBold
read more: prefixInheritedBold, URL underlined
reason textInheritedNormal
Connectors (├──, )InheritedDim

Connector types

The tree uses arrow connectors (├─▸) for actionable items (hint, fix, link) and line connectors (├──) for informational items (reason).

Plain text fallback

When tree structure is disabled (piped output, browser), sections are indented with two spaces:

error: VercelError [database:pool_exhausted] Database connection pool exhausted
  All 20 connections are in use and none have been released.
  hint: Consider using pgBouncer for connection pooling.
  fix: Increase max_connections or add pgBouncer.
  read more: https://vercel.com/docs/storage/neon#connection-pooling

Format primitives

The @vercel/error/format entry point exports functions for building custom formatted output outside of VercelError. Use these when formatting errors you don't own or building custom CLI output.

import { frame, hint, fix, link } from '@vercel/error/format';

const output = frame('Build failed: missing entry point', [
  'No index.ts or index.js found in the src/ directory.',
  hint('Check your tsconfig.json paths configuration.'),
  fix('Create src/index.ts or update the "main" field in package.json.'),
  link('https://vercel.com/docs/builds#entry-points'),
]);

console.error(output);

All format functions are nil-safe: pass undefined or null and they return undefined, which frame filters out. frame auto-detects formatting the same way VercelError.toString() does.

HTTP error responses

errorResponse builds a complete HTTP error response with content negotiation. It returns { status, body, headers } for use with any framework.

import { errorResponse } from '@vercel/error/server';

const { status, body, headers } = errorResponse(error);
return new Response(body, { status, headers });

Content negotiation

Pass a Request or HeadersLike object as the second argument to enable content negotiation. The response body switches from JSON to structured ANSI text when the client signals a preference:

const { status, body, headers } = errorResponse(error, request);
return new Response(body, { status, headers });

Clients signal ANSI preference in three ways (checked in order):

SignalExample
X-Error-Format: ansi headercurl -H "X-Error-Format: ansi"
Accept: text/plain+ansi headerCustom client header
User-Agent containing curl/Auto-detected for curl users

Without a signal (or without a request object), the response is always JSON.

Plain parameters

You can pass plain parameters instead of a VercelError instance.

import { errorResponse } from '@vercel/error/server';

const { status, body, headers } = errorResponse({
  status: 429,
  code: 'rate_limited',
  message: 'Too many requests',
  hint: 'Wait 60 seconds before retrying.',
});

return new Response(body, { status, headers });

Wire format

The JSON response body follows a canonical shape:

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests",
    "reason": "You exceeded 100 requests per minute.",
    "hint": "Wait 60 seconds before retrying.",
    "fix": "Implement exponential backoff in your client.",
    "link": "https://vercel.com/docs/limits#rate-limits"
  }
}

All fields except message are optional. When using a VercelError, userMessage is used for message in the response, falling back to error.message when userMessage isn't set.

Manual ANSI detection

Use wantsAnsi directly when you need to check ANSI preference outside of errorResponse:

import { wantsAnsi } from '@vercel/error/server';

if (wantsAnsi(request)) {
  // render ANSI-formatted output
}

Accepts a Request, any HeadersLike object, or null/undefined.

Parsing error responses

Validate unknown JSON

parseErrorResponse validates that unknown data matches the ErrorResponse shape. Returns the validated response or undefined if invalid.

import { parseErrorResponse } from '@vercel/error/client';

const res = await fetch('/api/deploy');
if (!res.ok) {
  const parsed = parseErrorResponse(await res.json());
  if (parsed) {
    console.log(parsed.error.code, parsed.error.message);
  }
}

Reconstruct a VercelError

fromErrorResponse reconstructs a VercelError from a validated ErrorResponse. Use this at service boundaries when you want to re-throw, enrich, or chain an upstream error.

import { parseErrorResponse, fromErrorResponse } from '@vercel/error/client';

const res = await fetch('https://api.vercel.com/v1/deployments');
if (!res.ok) {
  const parsed = parseErrorResponse(await res.json());
  if (parsed) {
    throw fromErrorResponse(parsed, {
      statusCode: res.status,
      scope: 'upstream',
      cause: new Error(`${res.url} returned ${res.status}`),
    });
  }
}

The response message becomes both the VercelError message and userMessage, since it was already client-safe on the wire.

Error utilities

Guards and extractors for working with errors from any source. All exported from @vercel/error.

FunctionDescription
isVercelError(value)Type guard for VercelError. Uses instanceof with a Symbol.for fallback for cross-realm detection
isError(value)Type guard for Error. Handles cross-realm errors via prototype walking
isErrorLike(value)Type guard for objects with a message string property
hasCode(error, code)Check if an error has a specific code. Also accepts an array of codes
getMessage(error, fallback?)Extract a message from any value. Returns undefined when no message is found and no fallback is provided
getRootCause(error)Walk the cause chain to the root. Handles cycles via WeakSet

Subclassing

VercelError is designed for subclassing. The Custom error classes section above shows how to use subclasses with createErrors — this section covers the general pattern.

Two things to keep in mind:

  • Hardcode this.name in your constructor. Bundler minification turns class names into single letters, which breaks Sentry grouping and log readability. String literals are minification-safe.

  • Accept VercelErrorOptions as the options type so your subclass stays compatible with createErrors.

import { VercelError } from '@vercel/error';
import type { VercelErrorOptions } from '@vercel/error';

class NetworkError extends VercelError {
  readonly retryable: boolean;

  constructor(
    message: string,
    options?: VercelErrorOptions & { retryable?: boolean },
  ) {
    super(message, options);
    this.name = 'NetworkError';
    this.retryable = options?.retryable ?? false;
  }
}

const error = new NetworkError('Connection refused', {
  code: 'conn_refused',
  retryable: true,
});

error.name; // 'NetworkError' (stable across minification)
error.retryable; // true
error.toString(); // uses NetworkError in the header

FAQs

Package last updated on 25 Jun 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts