Err
Error handling utilities for robust error management.
Provides utilities for error inspection, stack trace manipulation, try-catch wrappers, type guards, and null safety. Features formatted error logging and error wrapping utilities.
Import
import { Err } from '@wollybeard/kit'
import * as Err from '@wollybeard/kit/err'
Conversion
[F]
ensure
(value: unknown): Error
Ensure that the given value is an error and return it. If it is not an error than wrap it in one, passing the given value as the error message.
Inspection
[F]
log
(error: Error, options?: { color?: boolean; stackTraceColumns?: number; identColumns?: number; maxFrames?: number; showHelp?: boolean; } | undefined): void
Log an error to console with nice formatting.
[T]
InspectOptions
type InspectOptions = InferOptions<typeof optionSpecs>
Options for configuring error inspection output. All options can be overridden via environment variables.
color
- Whether to use ANSI color codes for better readability (default: true, env: ERROR_DISPLAY_COLOR)
stackTraceColumns
- Maximum column width before truncating stack trace lines (default: 120, env: ERROR_DISPLAY_STACK_TRACE_COLUMNS)
identColumns
- Number of spaces to use for indentation (default: 4, env: ERROR_DISPLAY_IDENT_COLUMNS)
maxFrames
- Maximum number of stack frames to show; 0 to hide stack traces entirely (default: 10, env: ERROR_DISPLAY_MAX_FRAMES)
showHelp
- Whether to display the environment variable help section (default: true, env: ERROR_DISPLAY_SHOW_HELP)
Examples:
// Use default options
Err.inspect(error)
// Customize options
Err.inspect(error, {
color: false,
stackTraceColumns: 200,
showHelp: false,
})
// Hide stack traces (useful for test snapshots)
Err.inspect(error, { maxFrames: 0, showHelp: false, color: false })
// Set via environment variables
process.env.ERROR_DISPLAY_COLOR = 'false'
process.env.ERROR_DISPLAY_SHOW_HELP = 'false'
[F]
inspect
(error: Error, options?: { color?: boolean; stackTraceColumns?: number; identColumns?: number; maxFrames?: number; showHelp?: boolean; } | undefined): string
Parameters:
error
- The error to inspectoptions
- Optional configuration for formatting
Returns: A formatted string representation of the error
Render an error to a string with detailed formatting.
Features:
- Nested error support (causes and aggregate errors)
- Context object formatting
- Stack trace cleaning with filtering indicators
- Tree-like visual guides for nested structures
- Configurable via options or environment variables
Examples:
// Simple error
const error = new Error('Something went wrong')
console.log(Err.inspect(error))
// Error with context
const contextError = new Error('API failed')
contextError.context = { userId: 123, endpoint: '/api/users' }
console.log(Err.inspect(contextError))
// Aggregate error with multiple failures
const errors = [
new Error('Database connection failed'),
new Error('Redis timeout'),
]
const aggregate = new AggregateError(errors, 'Multiple services failed')
console.log(Err.inspect(aggregate))
// Disable help section
console.log(Err.inspect(error, { showHelp: false }))
Stack Traces
[I]
StackOptions
interface StackOptions {
/**
* Remove internal library frames from the stack trace.
* @default true
*/
removeInternal?: boolean
/**
* Patterns to filter out from stack traces.
* @default ['node_modules', 'node:internal']
*/
filterPatterns?: string[]
/**
* Maximum number of frames to show.
* @default 10
*/
maxFrames?: number
/**
* Include source code context around error location.
* @default false
*/
includeSource?: boolean
/**
* Number of source lines to show before and after error.
* @default 2
*/
sourceContext?: number
}
Options for cleaning and formatting stack traces.
[I]
StackFrame
interface StackFrame {
/**
* Function name or <anonymous>
*/
function: string
/**
* File path
*/
file: string
/**
* Line number
*/
line: number
/**
* Column number
*/
column: number
/**
* Whether this is internal to the library
*/
isInternal: boolean
/**
* Whether this is a native V8 frame
*/
isNative: boolean
/**
* Raw frame string
*/
raw: string
}
Parsed stack frame information.
[F]
parseStack
(stack: string): StackFrame[]
Parse a stack trace string into structured frames.
[I]
StackCleanStats
interface StackCleanStats {
/**
* Total number of frames before filtering.
*/
totalFrames: number
/**
* Number of frames filtered out.
*/
filteredFrames: number
/**
* Number of node_modules frames filtered.
*/
nodeModulesFrames: number
/**
* Number of internal frames filtered.
*/
internalFrames: number
/**
* Number of frames shown.
*/
shownFrames: number
/**
* Whether the output was truncated due to maxFrames.
*/
wasTruncated: boolean
}
Statistics about stack trace filtering. Provides detailed information about what was filtered during stack cleaning.
Examples:
const result = cleanStackWithStats(error.stack)
console.log(`Filtered ${result.stats.filteredFrames} frames`)
console.log(
`Showing ${result.stats.shownFrames} of ${result.stats.totalFrames} total`,
)
[I]
CleanStackResult
interface CleanStackResult {
/**
* The cleaned stack trace string.
*/
stack: string
/**
* Statistics about what was filtered.
*/
stats: StackCleanStats
}
Result of cleaning a stack trace. Contains both the cleaned stack string and statistics about what was filtered.
[F]
cleanStackWithStats
(stack: string, options?: StackOptions | undefined): CleanStackResult
Parameters:
stack
- The raw stack trace string to cleanoptions
- Optional configuration for filtering and formatting
Returns: Object containing cleaned stack and filtering statistics
Clean a stack trace by removing internal frames and applying filters. Returns both the cleaned stack and detailed statistics about filtering.
Examples:
const error = new Error('Something failed')
const result = Err.cleanStackWithStats(error.stack, {
removeInternal: true,
filterPatterns: ['node_modules'],
maxFrames: 10,
})
console.log(result.stack) // Cleaned stack trace
console.log(`Filtered ${result.stats.nodeModulesFrames} node_modules frames`)
[F]
cleanStack
(stack: string, options?: StackOptions | undefined): string
Parameters:
stack
- The raw stack trace string to cleanoptions
- Optional configuration for filtering
Returns: The cleaned stack trace string
DEPRECATED
Use cleanStackWithStats for detailed filtering information
Clean a stack trace by removing internal frames and applying filters.
[F]
formatFrame
(frame: StackFrame): string
Format a stack frame for better readability.
[Class]
CleanError
class {
constructor(message: string, options?: (ErrorOptions & { context?: object; stackOptions?: StackOptions; }) | undefined)
// Properties
originalStack?: string | undefined
context?: object | undefined
}
Properties:
originalStack
- Original uncleaned stack trace.context
- Additional context for the error.
Enhanced Error class that automatically cleans stack traces.
[F]
mergeStacks
(wrapper: Error, cause: Error): string
Merge stack traces from multiple errors (useful for wrapped errors). This preserves the full error chain while removing duplicates.
Try-Catch
[F]
tryCatch
<returned, thrown > (promise: Promise<returned>, predicates ?: readonly[TypePredicate<thrown>, ...TypePredicate < thrown > []] | undefined): Promise < returned | (IsUnknown<thrown> extends true ? Error : thrown)>
<returned, thrown > (fn: () => returned, predicates ?: readonly[TypePredicate<thrown>, ...TypePredicate < thrown > []] | undefined): AwaitedUnion < returned, IsUnknown<thrown> extends true ? Error : thrown >
<returned, thrown > (fnOrPromise: Promise<any> | (() => returned), predicates ?: readonly[TypePredicate<thrown>, ...TypePredicate < thrown > []] =[
is as Bool.TypePredicate<thrown>,
]): any
Parameters:
predicates
- Type predicates to filter which errors to catch (defaults to all Error instances)
Returns: The result if successful, or the caught error
Try to execute a function or resolve a promise, catching errors instead of throwing. Returns either the successful result or the caught error.
Examples:
// With function
const result = Err.tryCatch(() => JSON.parse(input)) // parsed value | Error
// With promise
const data = await Err.tryCatch(fetch(url)) // Response | Error
// With custom predicates
const isNetworkError = (e: unknown): e is NetworkError =>
e instanceof Error && e.name === 'NetworkError'
const response = Err.tryCatch(
() => fetch(url),
[isNetworkError],
) // Response | NetworkError
[T]
TryCatchDefaultPredicateTypes
type TryCatchDefaultPredicateTypes = Error
Default error types caught by try/catch functions when no predicates are specified.
[F]
tryCatchify
<fn extends Fn.AnyAny, thrown > (fn: fn, predicates ?: readonly[TypePredicate<thrown>, ...TypePredicate < thrown > []] =[is as Bool.TypePredicate<thrown>]): (...args: Parameters<fn>) => AwaitedUnion<ReturnType<fn>, IsUnknown<thrown> extends true ? Error : thrown>
Parameters:
fn
- The function to transformpredicates
- Type predicates to filter which errors to catch (defaults to all Error instances)
Returns: A new function that returns results or errors instead of throwing
Transform a function to return caught errors instead of throwing them. The transformed function will return either the result or the caught error.
Examples:
// Transform a throwing function
const parseJsonSafe = Err.tryCatchify(JSON.parse)
const result = parseJsonSafe('{"valid": true}') // { valid: true }
const error = parseJsonSafe('invalid') // SyntaxError
// With custom error predicates
const isNetworkError = (e: unknown): e is NetworkError =>
e instanceof Error && e.name === 'NetworkError'
const fetchSafe = Err.tryCatchify(fetch, [isNetworkError])
const response = await fetchSafe(url) // Response | NetworkError
[F]
tryCatchIgnore
<$Return>(fn: () => $Return): $Return
Parameters:
fn
- The function to execute
Returns: The result of the function if successful, undefined otherwise
Try to execute a function and silently ignore any errors. Returns the result if successful, or undefined if it throws. For async functions, errors are silently caught without rejection.
Examples:
// Sync function
Err.tryCatchIgnore(() => JSON.parse(invalidJson)) // returns undefined
// Async function
await Err.tryCatchIgnore(async () => {
throw new Error('Network error')
}) // returns undefined, no rejection
Try-Or
[F]
tryOrRethrow
<$Return>(fn: () => $Return, wrapper: string | WrapOptions | ((cause: Error) => Error)): $Return extends Promise<any> ? $Return : $Return
Parameters:
fn
- The function to executewrapper
- Either a string message, options object, or a function that wraps the error
Returns: The result of the function if successful
Throws:
- The wrapped error if the function throws
Try to execute a function and wrap any thrown errors with a higher-level message. Handles both synchronous and asynchronous functions automatically.
Examples:
// Simple string message
const data = await Err.tryOrRethrow(
fetchData,
'Failed to fetch data',
)
// With options
const user = await Err.tryOrRethrow(
() => fetchUser(userId),
{ message: 'Failed to fetch user', context: { userId } },
)
// With wrapper function
const result = await Err.tryOrRethrow(
riskyOperation,
wrapWith('Operation failed'),
)
// Custom error wrapper
const config = await Err.tryOrRethrow(
loadConfig,
(cause) => new ConfigError('Failed to load config', { cause }),
)
[F]
tryAllOrRethrow
<$Fns extends readonly [() => any, ...Array<() => any>]>(fns: $Fns, wrapper: string | WrapOptions | ((cause: Error) => Error)): Promise<{ [K in keyof $Fns]: Awaited<ReturnType<$Fns[K]>>; }>
Parameters:
fns
- Array of functions to executewrapper
- Either a string message, options object, or a function that wraps the error
Returns: Array of results if all succeed
Throws:
- AggregateError with wrapped individual errors if any fail
Try multiple functions and wrap any errors with a higher-level message. If any function throws, all errors are collected into an AggregateError.
Examples:
const [users, posts] = await Err.tryAllOrRethrow(
[fetchUsers, fetchPosts],
'Failed to load data',
)
// With context
const [config, schema, data] = await Err.tryAllOrRethrow(
[loadConfig, loadSchema, loadData],
{ message: 'Failed to initialize', context: { env: 'production' } },
)
[F]
tryOr
<success, fallback > (fn: () => success, fallback: LazyMaybe<fallback>): TryOrReturn<success, fallback>
Parameters:
fn
- The function to executefallback
- The fallback value or function (must be sync if fn is sync)
Returns: The result of the function if successful, or the fallback value if it throws
Try to execute a function and return a fallback value if it throws.
Type constraints:
- If
fn
is synchronous,fallback
must also be synchronous - If
fn
is asynchronous,fallback
can be either sync or async - For sync functions with async fallbacks, use tryOrAsync instead
Examples:
// Sync function with sync fallback
const data = Err.tryOr(
() => JSON.parse(input),
{ error: 'Invalid JSON' },
)
// Async function with sync fallback
const config = await Err.tryOr(
async () => loadConfig(),
() => getDefaultConfig(),
)
// Async function with async fallback
const data = await Err.tryOr(
async () => fetchFromPrimary(),
async () => fetchFromSecondary(),
)
// This would be a TYPE ERROR:
// const bad = Err.tryOr(
// () => 42, // sync
// async () => 'fallback' // async - not allowed!
// )
[F]
tryOrAsync
<success, fallback > (fn: () => success, fallback: LazyMaybe<fallback>): Promise<Awaited<success> | Awaited<fallback>>
Parameters:
fn
- The function to execute (sync or async)fallback
- The fallback value or function (sync or async)
Returns: Always returns a Promise of the result or fallback
Try to execute a function and return a fallback value if it throws. Always returns a Promise, allowing async fallbacks for sync functions.
Use this when:
- You have a sync function with an async fallback
- You want consistent async behavior regardless of input types
Examples:
// Sync function with async fallback
const data = await Err.tryOrAsync(
() => readFileSync('config.json'),
async () => fetchDefaultConfig(),
)
// Ensures consistent Promise return
const result = await Err.tryOrAsync(
() => 42,
() => 'fallback',
) // Always Promise<number | string>
[F]
tryOrAsyncOn
<success>(fn: () => success): <fallback>(fallback: LazyMaybe<fallback>) => Promise<Awaited<success> | Awaited<fallback>>
Curried version of tryOrAsync that takes the function first. Useful for creating reusable async error handlers.
Examples:
const parseJsonOrFetch = Err.tryOrAsyncOn(() => JSON.parse(input))
const data = await parseJsonOrFetch(async () => fetchDefault())
[F]
tryOrAsyncWith
<fallback>(fallback: LazyMaybe<fallback>): <success>(fn: () => success) => Promise<Awaited<success> | Awaited<fallback>>
Curried version of tryOrAsync that takes the fallback first. Always returns a Promise regardless of input types.
Examples:
const orFetchDefault = Err.tryOrAsyncWith(async () => fetchDefault())
const data1 = await orFetchDefault(() => localData())
const data2 = await orFetchDefault(() => cachedData())
[F]
tryOrOn
<success>(fn: () => success): <fallback>(fallback: LazyMaybe<fallback>) => TryOrReturn<success, fallback>
Curried version of tryOr that takes the function first. Useful for creating reusable error handlers.
Note: Same type constraints as tryOr apply
- sync functions require sync fallbacks.
Examples:
const parseJsonOr = Err.tryOrOn(() => JSON.parse(input))
const data = parseJsonOr({ error: 'Invalid JSON' })
[F]
tryOrWith
<fallback>(fallback: LazyMaybe<fallback>): <success>(fn: () => success) => TryOrReturn<success, fallback>
Curried version of tryOr that takes the fallback first. Useful for creating reusable fallback patterns.
Note: Same type constraints as tryOr apply
- sync functions require sync fallbacks.
Examples:
const orDefault = Err.tryOrWith({ status: 'unknown', data: null })
const result1 = orDefault(() => fetchStatus())
const result2 = orDefault(() => getLatestData())
[C]
tryOrUndefined
;(<success>(fn: () => success) => TryOrReturn<success, undefined>)
Try to execute a function and return undefined if it throws. Shorthand for tryOrWith(undefined)
.
Examples:
const data = Err.tryOrUndefined(() => localStorage.getItem('key'))
// data is string | undefined
[C]
tryOrNull
;(<success>(fn: () => success) => TryOrReturn<success, null>)
Try to execute a function and return null if it throws. Shorthand for tryOrWith(null)
.
Examples:
const user = await Err.tryOrNull(async () => fetchUser(id))
// user is User | null
Type Guards
[F]
is
(value: unknown): boolean
Parameters:
value
- The value to check
Returns: True if the value is an Error instance
Type predicate to check if a value is an Error instance.
Examples:
Err.is(new Error('test')) // true
Err.is('not an error') // false
Err.is(null) // false
[F]
isAggregateError
(value: unknown): boolean
Check if a value is an AggregateError instance.
[F]
isAbortError
(error: any): boolean
Parameters:
error
- The error to check
Returns: True if the error is an AbortError
Check if an error is an AbortError (from AbortController/AbortSignal).
Examples:
const controller = new AbortController()
controller.abort()
try {
await fetch(url, { signal: controller.signal })
} catch (error) {
if (Err.isAbortError(error)) {
console.log('Request was aborted')
}
}
Types
[∩]
ContextualError
type ContextualError<
$Context extends Record<string, unknown> = Record<string, unknown>,
> = Error & {
context: $Context
}
An error with additional contextual data.
[T]
Context
type Context = object
Context information that can be attached to errors. Must be an object to ensure it can be properly serialized and inspected.
[I]
ErrorWithContext
interface ErrorWithContext extends Error {
/**
* Additional context information about the error.
*/
context?: Context
}
DEPRECATED
Use ContextualError instead for better type safety.
An error that includes additional context information.
Utilities
[F]
throwNull
<V>(value: V, message ?: string | undefined): Exclude<V, null>
Parameters:
value
- The value to checkmessage
- Optional custom error message
Returns: The value if not null
Throws:
- Error if the value is null
Throw an error if the value is null, otherwise return the non-null value.
Examples:
const result = Err.throwNull(maybeNull) // throws if null
const safe = Err.throwNull(maybeNull, 'Custom error message')
[C]
defaultThrowNullMessage
'Unexpected null value.'
Default error message used by throwNull when no custom message is provided.
[F]
guardNull
<fn extends Fn.AnyAny>(fn: fn, message ?: string | undefined): ReturnExclude<null, fn>
Parameters:
fn
- The function to wrapmessage
- Optional custom error message when null is returned
Returns: A wrapped function that throws on null return values
Wrap a function to throw an error if it returns null.
Examples:
const find = (id: string) => items.find(item => item.id === id) ?? null
const findOrThrow = Err.guardNull(find, 'Item not found')
const item = findOrThrow('123') // throws if not found
[F]
createContextualError
<$Context extends Record<string, unknown>>(message: string, context: $Context): ContextualError<$Context>
Parameters:
message
- The error messagecontext
- Contextual data to attach to the error
Returns: An Error instance with the context attached
Create an error with contextual data about it.
The context object is attached to the error instance and the message property is made enumerable for better debugging experience.
Examples:
const error = Err.createContextualError('Failed to fetch user', {
userId: '123',
endpoint: '/api/users',
statusCode: 404,
})
console.log(error.context.userId) // '123'
Wrapping
[I]
WrapOptions
interface WrapOptions {
/**
* The error message for the wrapper error.
*/
message: string
/**
* Additional context to attach to the error.
*/
context?: Context
}
Options for wrapping errors with additional context.
[F]
wrap
(cause: unknown, messageOrOptions: string | WrapOptions): Error
Parameters:
cause
- The error to wrap (will be set as the cause)messageOrOptions
- Either a string message or options with message and context
Returns: A new Error with the given message and the original error as cause
Wrap an error with a higher-level error message. If the input is not an Error, it will be converted to one using ensure.
Examples:
try {
await fetchData()
} catch (error) {
throw Err.wrap(error, 'Failed to fetch data')
}
// With context
try {
await fetchUser(userId)
} catch (error) {
throw Err.wrap(error, {
message: 'Failed to fetch user',
context: { userId },
})
}
[C]
wrapOn
;((cause: unknown) => (messageOrOptions: string | WrapOptions) => Error)
Curried version of wrap that takes the error first. Useful for error handling pipelines.
Examples:
const wrapFetchError = Err.wrapOn(networkError)
throw wrapFetchError('Failed to fetch data')
[C]
wrapWith
;((messageOrOptions: string | WrapOptions) => (cause: unknown) => Error)
Curried version of wrap that takes the message/options first. Useful for creating reusable error wrappers.
Examples:
const wrapAsFetchError = Err.wrapWith('Failed to fetch data')
try {
await fetchData()
} catch (error) {
throw wrapAsFetchError(error)
}
// With context
const wrapAsUserError = Err.wrapWith({
message: 'Failed to process user',
context: { operation: 'update' },
})
Other
[T]
_InferOptions
type _InferOptions<
$EnvironmentConfigurableOptions extends EnvironmentConfigurableOptionSpec[],
> = {
[i in keyof $EnvironmentConfigurableOptions]: {
[_ in $EnvironmentConfigurableOptions[i]['name']]?: ReturnType<
$EnvironmentConfigurableOptions[i]['parse']
>
}
}
[F]
captureStackTrace
(message?: string = 'Captured stack'): string
Capture the current stack trace at a specific point. Useful for adding trace information without throwing.
[F]
getCaller
(depth?: number = 1): StackFrame | undefined
Get the caller information from the current stack.
[F]
tryCatch
<returned, thrown > (promise: Promise<returned>, predicates ?: readonly[TypePredicate<thrown>, ...TypePredicate < thrown > []] | undefined): Promise < returned | (IsUnknown<thrown> extends true ? Error : thrown)>
<returned, thrown > (fn: () => returned, predicates ?: readonly[TypePredicate<thrown>, ...TypePredicate < thrown > []] | undefined): AwaitedUnion < returned, IsUnknown<thrown> extends true ? Error : thrown >
<returned, thrown > (fnOrPromise: Promise<any> | (() => returned), predicates ?: readonly[TypePredicate<thrown>, ...TypePredicate < thrown > []] =[
is as Bool.TypePredicate<thrown>,
]): any
Parameters:
predicates
- Type predicates to filter which errors to catch (defaults to all Error instances)
Returns: The result if successful, or the caught error
[F]
tryCatch
<returned, thrown > (promise: Promise<returned>, predicates ?: readonly[TypePredicate<thrown>, ...TypePredicate < thrown > []] | undefined): Promise < returned | (IsUnknown<thrown> extends true ? Error : thrown)>
<returned, thrown > (fn: () => returned, predicates ?: readonly[TypePredicate<thrown>, ...TypePredicate < thrown > []] | undefined): AwaitedUnion < returned, IsUnknown<thrown> extends true ? Error : thrown >
<returned, thrown > (fnOrPromise: Promise<any> | (() => returned), predicates ?: readonly[TypePredicate<thrown>, ...TypePredicate < thrown > []] =[
is as Bool.TypePredicate<thrown>,
]): any
Parameters:
predicates
- Type predicates to filter which errors to catch (defaults to all Error instances)
Returns: The result if successful, or the caught error