Custom error classes exported
Why it matters
When every error a package throws is a plain Error with a string message, consumers are forced to parse error messages with regex or includes() checks — fragile code that breaks the moment you change wording. ISO 25010 fault-tolerance and testability both suffer: you cannot write a catch (e) { if (e instanceof NetworkError) guard without custom error types. For SDK packages that wrap external APIs, distinguishing a 429 from a 500 from a network timeout requires typed errors. Without them, consumers either swallow all errors or crash on recoverable conditions.
Severity rationale
High because generic `Error` instances force consumers into string-parsing for error discrimination, producing fragile error handling that breaks on any message-text change.
Remediation
Define and export a typed error hierarchy from src/errors.ts, then re-export it from your package entry point.
// src/errors.ts
export class SdkError extends Error {
constructor(message: string, public readonly code: string) {
super(message)
this.name = 'SdkError'
}
}
export class ApiError extends SdkError {
constructor(message: string, public readonly statusCode: number) {
super(message, 'API_ERROR')
this.name = 'ApiError'
}
}
export class RateLimitError extends ApiError {
constructor(public readonly retryAfter: number) {
super('Rate limit exceeded', 429)
this.name = 'RateLimitError'
}
}
// src/index.ts
export { SdkError, ApiError, RateLimitError } from './errors'
Consumers can then guard: if (e instanceof RateLimitError) { await sleep(e.retryAfter * 1000) }.
Detection
-
ID:
error-classes -
Severity:
high -
What to look for: Enumerate every error thrown by the SDK. For each error, search the codebase for error handling patterns:
- Custom error classes that extend
Error(e.g.,class ApiError extends Error) - Whether those custom errors are exported from the public API entry point
- Whether errors thrown by the package are distinguishable from generic
Errorinstances - Whether error objects include useful properties (status code, error code, original error)
For Python: check for custom exception classes. For Rust: check for custom error enums implementing
std::error::Error.
- Custom error classes that extend
-
Pass criteria: The package defines at least one custom error class (or typed error) that extends the language's base error type, includes useful context (error code, message, cause), and is exported from the public API. Consumers can catch specific errors:
catch (e) { if (e instanceof YourApiError) { ... } }— 100% of errors thrown by the SDK must be custom Error subclasses with descriptive names. Report: "X error types found, all Y are custom error classes." -
Fail criteria: The package throws only generic
Error('message')orthrow new Error(...)with no custom types. Consumers cannot programmatically distinguish between error types and must parse error message strings. -
Skip (N/A) when: The package is a pure utility library with no async operations, no I/O, and no failure modes beyond invalid arguments (e.g., a math library, a string formatting library). If the package only validates inputs, throwing
TypeErroris acceptable. -
Detail on fail:
"All errors thrown are generic Error instances with string messages. The API client throws 'Error: Request failed' for both network errors and 4xx responses. Consumers cannot distinguish between error types without parsing the message string." -
Remediation: Custom error classes let consumers handle failures programmatically. This is especially important for SDK packages that wrap APIs.
// src/errors.ts: export class SdkError extends Error { constructor(message: string, public readonly code: string) { super(message) this.name = 'SdkError' } } export class ApiError extends SdkError { constructor( message: string, public readonly statusCode: number, public readonly response?: unknown ) { super(message, 'API_ERROR') this.name = 'ApiError' } } export class RateLimitError extends ApiError { constructor(public readonly retryAfter: number) { super('Rate limit exceeded', 429) this.name = 'RateLimitError' } }// src/index.ts — export them: export { SdkError, ApiError, RateLimitError } from './errors'
External references
- iso-25010:2011 · reliability.fault-tolerance — Fault tolerance — typed errors enable consumer error handling
- iso-25010:2011 · maintainability.testability — Testability — distinguishable error types simplify assertions
Taxons
History
- 2026-04-18·v1.0.0·Initial import from sdk-package-quality·automated