npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@atmaca/errors

v0.1.0

Published

Atmaca Errors Package

Readme

@atmaca/errors

Dual-Protocol Error Classes for REST and JSON-RPC Services

The @atmaca/errors package provides a comprehensive set of error classes designed for modern microservices architectures. Each error includes both REST HTTP status codes and JSON-RPC 2.0 error codes, enabling seamless error translation across protocols.


Features

  • Dual-Protocol Support: Every error includes both REST and JSON-RPC codes
  • Rich Context: Metadata support for detailed error information
  • Machine-Readable: Structured reason codes for programmatic handling
  • Human-Friendly: Clear, descriptive error messages
  • Framework Agnostic: Works with any Node.js framework
  • Zero Dependencies: Lightweight and secure

Installation

npm install @atmaca/errors

Quick Start

import { NotFoundError, ValidationError } from '@atmaca/errors'

// Throw a not found error
const user = await db.users.findById(userId)
if (!user) {
  throw new NotFoundError('User', { userId })
}

// Throw a validation error
if (age < 18) {
  throw new ValidationError('age', age, 'must be 18 or older')
}

Base Error Classes

RuntimeError

The base class for all runtime errors in Atmaca applications.

class RuntimeError extends Error {
  name        // Error class name (e.g., "NotFoundError")
  reason      // Machine-readable reason code (e.g., "user_not_found")
  message     // Human-readable error message
  restCode    // HTTP status code (e.g., 404)
  jsonRpcCode // JSON-RPC 2.0 error code (e.g., -32002)
  metadata    // Additional context data
}

Example:

import { NotFoundError } from '@atmaca/errors'

const error = new NotFoundError('User', { userId: 123 })

console.log(error.name)        // "NotFoundError"
console.log(error.reason)      // "user_not_found"
console.log(error.message)     // "User not found"
console.log(error.restCode)    // 404
console.log(error.jsonRpcCode) // -32002
console.log(error.metadata)    // { userId: 123 }

InitializationError

Used for errors that occur during application initialization.

class InitializationError extends Error {
  name      // "InitializationError"
  reason    // Machine-readable reason code
  message   // Human-readable error message
  metadata  // Additional context data
}

Error Reference

JSON-RPC 2.0 Specific Errors

These errors align with the JSON-RPC 2.0 Specification:

ParseError

Invalid JSON received by the server.

import { ParseError } from '@atmaca/errors'

throw new ParseError('Unexpected token at position 42')
// REST: 400, JSON-RPC: -32700

InvalidRequestError

The JSON sent is not a valid Request object.

import { InvalidRequestError } from '@atmaca/errors'

throw new InvalidRequestError('Missing "method" field')
// REST: 400, JSON-RPC: -32600

InvalidParamsError

Invalid method parameters.

import { InvalidParamsError } from '@atmaca/errors'

throw new InvalidParamsError('Parameter "userId" must be a number')
// REST: 400, JSON-RPC: -32602

InternalError

Internal JSON-RPC error.

import { InternalError } from '@atmaca/errors'

throw new InternalError('database connection', originalError)
// REST: 500, JSON-RPC: -32603

REST-Compliant Errors

Client Errors (4xx)

ValidationError

Input validation failed.

import { ValidationError } from '@atmaca/errors'

throw new ValidationError('email', 'invalid-email', 'must be a valid email address')
// REST: 400, JSON-RPC: -32001

// Properties
error.field      // 'email'
error.constraint // 'must be a valid email address'
NotFoundError

Resource not found.

import { NotFoundError } from '@atmaca/errors'

throw new NotFoundError('User', { userId: 123 })
// REST: 404, JSON-RPC: -32002

// Properties
error.entity   // 'User'
error.reason   // 'user_not_found'
error.metadata // { userId: 123 }
NotAuthorizedError

Authentication required.

import { NotAuthorizedError } from '@atmaca/errors'

throw new NotAuthorizedError('User', 'token expired', { tokenId: 'abc123' })
// REST: 401, JSON-RPC: -32005
ForbiddenError

Action forbidden for user.

import { ForbiddenError } from '@atmaca/errors'

throw new ForbiddenError('User', 'delete account', { userId: 123 })
// REST: 403, JSON-RPC: -32004

// Properties
error.entity // 'User'
error.action // 'delete account'
ConflictError

Resource conflict (e.g., duplicate entry).

import { ConflictError } from '@atmaca/errors'

throw new ConflictError('User', 'email already exists', { email: '[email protected]' })
// REST: 409, JSON-RPC: -32003

// Properties
error.entity         // 'User'
error.conflictReason // 'email already exists'
PreconditionFailedError

Precondition not met.

import { PreconditionFailedError } from '@atmaca/errors'

throw new PreconditionFailedError('If-Match header must match current ETag')
// REST: 412, JSON-RPC: -32007

// Properties
error.condition // 'If-Match header must match current ETag'
PayloadTooLargeError

Request body too large.

import { PayloadTooLargeError } from '@atmaca/errors'

throw new PayloadTooLargeError(5242880, 1048576) // 5MB vs 1MB limit
// REST: 413, JSON-RPC: -32009

// Properties
error.size    // 5242880
error.maxSize // 1048576
UnsupportedMediaTypeError

Content type not supported.

import { UnsupportedMediaTypeError } from '@atmaca/errors'

throw new UnsupportedMediaTypeError('text/xml', ['application/json', 'application/x-www-form-urlencoded'])
// REST: 415, JSON-RPC: -32008

// Properties
error.providedType   // 'text/xml'
error.supportedTypes // ['application/json', 'application/x-www-form-urlencoded']
UnprocessableEntityError

Semantic validation error.

import { UnprocessableEntityError } from '@atmaca/errors'

throw new UnprocessableEntityError('invalid_date_range', 'Start date must be before end date')
// REST: 422, JSON-RPC: -32006
RateLimitExceededError

Too many requests.

import { RateLimitExceededError } from '@atmaca/errors'

throw new RateLimitExceededError(100, 60000, 30) // 100 requests per minute, retry after 30s
// REST: 429, JSON-RPC: -32010

// Properties
error.limit      // 100
error.windowMs   // 60000
error.retryAfter // 30
ResourceExhaustedError

Resource limit reached.

import { ResourceExhaustedError } from '@atmaca/errors'

throw new ResourceExhaustedError('database connections', 100, { current: 100 })
// REST: 429, JSON-RPC: -32011

// Properties
error.resource // 'database connections'
error.limit    // 100
BusinessRuleError

Business rule violation.

import { BusinessRuleError } from '@atmaca/errors'

throw new BusinessRuleError('minimum_order_value', 'Order total must be at least $10', { total: 5.99 })
// REST: 409, JSON-RPC: -32017

// Properties
error.rule        // 'minimum_order_value'
error.description // 'Order total must be at least $10'
GoneError

Resource permanently removed.

import { GoneError } from '@atmaca/errors'

throw new GoneError('API v1 endpoint', { deprecatedSince: '2024-01-01' })
// REST: 410, JSON-RPC: -32019

// Properties
error.resource // 'API v1 endpoint'

Server Errors (5xx)

FunctionExecutionError

Handler execution failed.

import { FunctionExecutionError } from '@atmaca/errors'

throw new FunctionExecutionError('getUser', 'handler', originalError, { userId: 123 })
// REST: 500, JSON-RPC: -32015

// Properties
error.fnName        // 'getUser'
error.phase         // 'handler'
error.originalError // originalError object
BadGatewayError

Upstream service error.

import { BadGatewayError } from '@atmaca/errors'

throw new BadGatewayError('payment-service', upstreamError, { requestId: 'req-123' })
// REST: 502, JSON-RPC: -32012

// Properties
error.upstream      // 'payment-service'
error.upstreamError // upstreamError object
ServiceUnavailableError

Service temporarily unavailable.

import { ServiceUnavailableError } from '@atmaca/errors'

throw new ServiceUnavailableError('database', 60) // retry after 60 seconds
// REST: 503, JSON-RPC: -32013

// Properties
error.service    // 'database'
error.retryAfter // 60
DependencyError

Required dependency unavailable.

import { DependencyError } from '@atmaca/errors'

throw new DependencyError('processPayment', 'stripe-api', { reason: 'connection timeout' })
// REST: 503, JSON-RPC: -32014

// Properties
error.fnName     // 'processPayment'
error.dependency // 'stripe-api'
NotImplementedError

Feature not yet implemented.

import { NotImplementedError } from '@atmaca/errors'

throw new NotImplementedError('bulk export', { requestedBy: 'user-123' })
// REST: 501, JSON-RPC: -32016

// Properties
error.feature // 'bulk export'
TimeoutError

Operation timed out.

import { TimeoutError } from '@atmaca/errors'

throw new TimeoutError('database query', 30000, { query: 'SELECT * FROM users' })
// REST: 504, JSON-RPC: -32018

// Properties
error.operation // 'database query'
error.timeoutMs // 30000

Special Errors

MethodNotFoundError

Method/function does not exist (extends NotFoundError).

import { MethodNotFoundError } from '@atmaca/errors'

throw new MethodNotFoundError('nonExistentFunction')
// REST: 404, JSON-RPC: -32601

TextErrorResponse

Generic error response with custom status code.

import { TextErrorResponse } from '@atmaca/errors'

throw new TextErrorResponse('Something went wrong', 500, originalError)
// REST: 500 (configurable)

// Properties
error.name     // 'TextErrorResponse'
error.restCode // 500 (or custom code)

Complete Error Reference Table

| Error Class | REST Code | JSON-RPC Code | Description | |-------------|-----------|---------------|-------------| | JSON-RPC Specific |||| | ParseError | 400 | -32700 | Invalid JSON received by server | | InvalidRequestError | 400 | -32600 | Invalid JSON-RPC Request object | | MethodNotFoundError | 404 | -32601 | Method/function does not exist | | InvalidParamsError | 400 | -32602 | Invalid method parameters | | InternalError | 500 | -32603 | Internal JSON-RPC error | | Client Errors (4xx) |||| | ValidationError | 400 | -32001 | Input validation failed | | NotFoundError | 404 | -32002 | Resource not found | | NotAuthorizedError | 401 | -32005 | Authentication required | | ForbiddenError | 403 | -32004 | Action forbidden for user | | ConflictError | 409 | -32003 | Resource conflict (e.g., duplicate) | | PreconditionFailedError | 412 | -32007 | Precondition not met | | PayloadTooLargeError | 413 | -32009 | Request body too large | | UnsupportedMediaTypeError | 415 | -32008 | Content type not supported | | UnprocessableEntityError | 422 | -32006 | Semantic validation error | | RateLimitExceededError | 429 | -32010 | Too many requests | | ResourceExhaustedError | 429 | -32011 | Resource limit reached | | BusinessRuleError | 409 | -32017 | Business rule violation | | GoneError | 410 | -32019 | Resource permanently removed | | Server Errors (5xx) |||| | FunctionExecutionError | 500 | -32015 | Handler execution failed | | InternalError | 500 | -32603 | Internal error | | NotImplementedError | 501 | -32016 | Feature not yet implemented | | BadGatewayError | 502 | -32012 | Upstream service error | | ServiceUnavailableError | 503 | -32013 | Service temporarily unavailable | | DependencyError | 503 | -32014 | Required dependency unavailable | | TimeoutError | 504 | -32018 | Operation timed out |


Usage Examples

REST API Error Handling

import express from 'express'
import { NotFoundError, ValidationError, NotAuthorizedError } from '@atmaca/errors'

const app = express()

app.get('/users/{id}', async (req, res) => {
  try {
    const user = await db.users.findById(req.params.id)

    if (!user) {
      throw new NotFoundError('User', { userId: req.params.id })
    }

    if (!req.user || req.user.id !== user.id) {
      throw new NotAuthorizedError('User', 'can only view own profile')
    }

    res.json(user)
  } catch (error) {
    // Use the REST code from the error
    res.status(error.restCode || 500).json({
      error: {
        name: error.name,
        reason: error.reason,
        message: error.message,
        metadata: error.metadata
      }
    })
  }
})

JSON-RPC Service Error Handling

import {
  ParseError,
  InvalidRequestError,
  MethodNotFoundError,
  InvalidParamsError
} from '@atmaca/errors'

async function handleJsonRpc(request) {
  try {
    // Parse JSON
    let parsed
    try {
      parsed = JSON.parse(request.body)
    } catch (e) {
      throw new ParseError('Invalid JSON format')
    }

    // Validate request structure
    if (!parsed.jsonrpc || !parsed.method) {
      throw new InvalidRequestError('Missing required fields')
    }

    // Check method exists
    if (!methods[parsed.method]) {
      throw new MethodNotFoundError(parsed.method)
    }

    // Execute method
    const result = await methods[parsed.method](parsed.params)

    return {
      jsonrpc: '2.0',
      result,
      id: parsed.id
    }
  } catch (error) {
    // Use the JSON-RPC code from the error
    return {
      jsonrpc: '2.0',
      error: {
        code: error.jsonRpcCode || -32603,
        message: error.message,
        data: error.metadata
      },
      id: parsed?.id || null
    }
  }
}

Best Practices

1. Always Include Metadata

// Good - includes context
throw new NotFoundError('User', { userId: 123, requestId: 'req-456' })

// Avoid - missing context
throw new NotFoundError('User')

2. Use Specific Error Classes

// Good - specific error
throw new ConflictError('User', 'email already exists', { email: '[email protected]' })

// Avoid - generic error
throw new Error('Conflict: email already exists')

3. Preserve Original Errors

// Good - includes original error
try {
  await upstream.call()
} catch (err) {
  throw new BadGatewayError('payment-service', err)
}

// Avoid - loses original context
try {
  await upstream.call()
} catch (err) {
  throw new BadGatewayError('payment-service')
}

4. Use Machine-Readable Reason Codes

// The reason property is automatically generated for most errors
const error = new NotFoundError('User')
console.log(error.reason) // 'user_not_found'

const error2 = new ConflictError('Email Address', 'already in use')
console.log(error2.reason) // 'email_address_conflict'

Protocol Bridging

Use errors seamlessly across different protocols:

import { NotFoundError } from '@atmaca/errors'

const error = new NotFoundError('Resource', { id: 123 })

// REST response
res.status(error.restCode).json({
  error: {
    message: error.message,
    reason: error.reason,
    metadata: error.metadata
  }
})

// JSON-RPC response
res.json({
  jsonrpc: '2.0',
  error: {
    code: error.jsonRpcCode,
    message: error.message,
    data: error.metadata
  },
  id: request.id
})

// GraphQL response
throw new GraphQLError(error.message, {
  extensions: {
    code: error.reason.toUpperCase(),
    http: { status: error.restCode },
    metadata: error.metadata
  }
})

TypeScript Support

TypeScript declarations are included:

import { NotFoundError, ValidationError, RuntimeError } from '@atmaca/errors'

function handleError(error: RuntimeError): void {
  console.log(error.name)        // string
  console.log(error.reason)      // string
  console.log(error.message)     // string
  console.log(error.restCode)    // number
  console.log(error.jsonRpcCode) // number
  console.log(error.metadata)    // any
}

License

MIT License