Validation

Handling Validation Errors

Catch and format input validation failures using ValidationError. Return structured JSON error envelopes with issue paths and messages.

When incoming request parameters, query strings, headers, or JSON bodies fail validation, Taser.js throws a ValidationError.


Anatomy of a ValidationError

The ValidationError class exposes the raw issues array returned by your schema validator:

class ValidationError extends Error {
  readonly issues: readonly StandardSchemaV1.Issue[];
}

Each issue typically contains:

  • path: Array of property keys or array indices indicating where the error occurred (for example, ["body", "email"] or ["query", "page"]).
  • message: Human-readable error description (for example, "Invalid email address").

Centralized Validation Error Handler

Configure your error envelope once in src/taser.ts using .onError():

src/taser.ts
import { createTaserApp, ValidationError, type InferAppContext } from "@taserjs/router";
import { internalServerError, unprocessableEntity } from "@taserjs/router/reply";
import { context } from "./context.js";

export default createTaserApp()
  .context(context)
  .onError((error) => {
    // 1. Handle Schema Validation Errors
    if (error instanceof ValidationError) {
      const formattedErrors: Record<string, string[]> = {};

      for (const issue of error.issues) {
        const fieldName = issue.path?.map(String).join(".") || "root";
        if (!formattedErrors[fieldName]) {
          formattedErrors[fieldName] = [];
        }
        formattedErrors[fieldName].push(issue.message);
      }

      return unprocessableEntity({
        status: 422,
        message: "The given data was invalid.",
        errors: formattedErrors,
      });
    }

    // 2. Handle Unexpected Server Errors
    console.error("[Unhandled Error]", error);
    return internalServerError({
      status: 500,
      message: "Internal Server Error",
    });
  });

Example Error Response

Given a request with invalid query parameters:

curl "http://localhost:3000/products?page=-5&category=unknown"

The client receives a clean, structured 422 Unprocessable Entity response:

{
  "status": 422,
  "message": "The given data was invalid.",
  "errors": {
    "query.page": ["Number must be greater than or equal to 1"],
    "query.category": ["Invalid enum value. Expected 'electronics' | 'clothing' | 'books'"]
  }
}

Auto-Generated 422 Response Schemas

When you attach input schemas (query, params, body) to a route, Taser.js automatically documents a 422 error contract for that endpoint. The typed client SDK also understands that the route can return 422 validation issues.