Error thrown when I try to console log a ZodError

# Postmortem: Error Thrown When Logging ZodError in TypeScript

## Summary
An attempt to log Zod validation errors using `console.log(error)` unexpectedly throws an error instead of printing diagnostic information. This occurs because `ZodError` contains circular references via its `.issues` array, causing JSON serialization to fail during console output.

## Root Cause
- **Circular references in ZodError**: The `ZodError` object links issues backwards to their parent error via a `.root` property, creating a circular structure that `console.log` cannot serialize.
- **Default JSON conversion**: Node.js's `console.log` implicitly tries to serialize objects using JSON-like methods, which fails on circular references.

## Why This Happens in Real Systems
- Zod errors often propagate through layers (UI, API, services) where developers log objects for debugging.
- Circumstances triggering Zod validation errors (e.g., invalid user input) are exactly when developers need informative logging.
- Production systems log errors centrally (Sentry, ELK) where serialization failures cause lost context.

## Real-World Impact
- **Debugging blindspots**: Critical validation error details disappear from logs.
- **Cascading failures**: An unhandled "Converting circular structure" exception might crash worker processes.
- **Delayed incident resolution**: Missing error context prolongs outage investigations.

## Example or Code

### ❌ Problematic Pattern
```typescript
import { z } from 'zod';

const schema = z.string();
const result = schema.safeParse(3);

// Throws "TypeError: Converting circular structure to JSON"
console.log(result.error);

✅ Correct Approach

// Log formatted errors instead of raw object
console.log(result.error.toString());

// Or serialize explicitly:
console.log(JSON.stringify(result.error, null, 2));

// Alternative: Use Zod's built-in formatting
console.log(result.error.format());

How Senior Engineers Fix It

  • Avoid raw logging: Never log ZodError instances directly. Always serialize explicitly:
    console.error(JSON.stringify(error, null, 2));
  • Use Zod’s formatters: Leverage error.format() for structured data or error.toString() for human-readable output.
  • Centralized exception handling: Wrap logging logic to automatically serialize errors before transmission:
    function safeLogError(error: unknown) {
      if (error instanceof z.ZodError) {
        console.error(JSON.stringify(error, null, 2));
      } else {
        console.error(error);
      }
    }
  • Type narrowing: Add explicit runtime checks for ZodError instances before handling.

Why Juniors Miss It

  • Assumption of safe serialization: Expecting console.log to handle complex objects like native types.
  • Untested error paths: Validation errors often occur outside happy-path testing scenarios.
  • TypeScript blind spots: Trusting type safety without considering runtime peculiarities of specific classes.
  • Documentation gaps: Zod’s API docs emphasize validation, not serialization pitfalls.