---
id: schema-handling-errors
title: Handling Parse Errors
category: getting-started
skillLevel: beginner
tags:
  - schema
  - errors
  - validation
  - error-handling
lessonOrder: 24
rule:
  description: >-
    Handle Parse Errors using Schema.
summary: >-
  When validation fails, you need to know what went wrong. The default parse error is detailed but hard to read. You need to extract useful error messages for logging, displaying to users, or debugging.
---

# Problem

When validation fails, you need to know what went wrong. The default parse error is detailed but hard to read. You need to extract useful error messages for logging, displaying to users, or debugging.

# Solution

```typescript
import { Schema, Effect, ParseResult } from "effect"

const User = Schema.Struct({
  name: Schema.String,
  age: Schema.Number,
  email: Schema.String,
})

// Three ways to handle parse errors:

// 1. SYNC WITH TRY/CATCH
const decodeSync = Schema.decodeUnknownSync(User)

function parseUserSync(data: unknown) {
  try {
    return decodeSync(data)
  } catch (error) {
    if (ParseResult.isParseError(error)) {
      console.log("Parse error:", ParseResult.TreeFormatter.formatErrorSync(error))
    }
    throw error
  }
}

// 2. SYNC RETURNING EITHER (no exceptions)
const decodeEither = Schema.decodeUnknownEither(User)

function parseUserSafe(data: unknown) {
  const result = decodeEither(data)
  
  if (result._tag === "Left") {
    const message = ParseResult.TreeFormatter.formatErrorSync(result.left)
    return { success: false, error: message }
  }
  
  return { success: true, user: result.right }
}

// 3. ASYNC WITH EFFECT (recommended)
const decodeEffect = Schema.decodeUnknown(User)

const parseUserEffect = (data: unknown) =>
  decodeEffect(data).pipe(
    Effect.mapError((error) => ({
      _tag: "ValidationError" as const,
      message: ParseResult.TreeFormatter.formatErrorSync(error),
    }))
  )

// Demo all three approaches
console.log("=== Sync with try/catch ===")
try {
  parseUserSync({ name: "Alice", age: "thirty", email: "a@b.com" })
} catch {
  // Error already logged
}

console.log("\n=== Sync returning Either ===")
const result = parseUserSafe({ name: 123, age: 30, email: "a@b.com" })
if (!result.success) {
  console.log("Validation failed:", result.error)
}

console.log("\n=== Async with Effect ===")
Effect.runPromise(
  parseUserEffect({ name: "Alice", age: 30 }).pipe(  // Missing email
    Effect.match({
      onFailure: (err) => console.log("Failed:", err.message),
      onSuccess: (user) => console.log("Success:", user.name),
    })
  )
)
```

# Why This Works

| Concept | Explanation |
|---------|-------------|
| **ParseResult.isParseError** | Type guard to check if error is from Schema |
| **TreeFormatter** | Human-readable error messages |
| **decodeUnknownEither** | Returns Either instead of throwing |
| **decodeUnknown** | Returns Effect for async composition |
| **Effect.mapError** | Transform error into your error type |

# When to Use

- Displaying validation errors to users
- Logging parse failures for debugging
- Building APIs that return structured error responses
- Composing validation with other effectful operations

# Related Patterns

- [Your First Schema](./hello-world.md)
- [Custom Error Messages](../error-handling/custom-messages.md)
- [Error Aggregation](../error-handling/error-aggregation.md)

