---
title: Error Handling
---

Ack provides detailed error information when validation fails. This guide explains how to interpret and use these errors.

## The `SchemaResult` Object

All `safeParse()` methods in Ack return a `SchemaResult` object. This object encapsulates either the successfully validated data or a `SchemaError`.

- `result.isOk`: Returns `true` if validation succeeded, `false` otherwise.
- `result.isFail`: Returns `true` if validation failed, `false` otherwise.
- `result.getOrThrow()`: Returns the validated data if `isOk` is true, otherwise throws an exception.
- `result.getOrNull()`: Returns the validated data if `isOk` is true, otherwise returns `null`.
- `result.getError()`: Returns the `SchemaError` if `isFail` is true, otherwise throws an exception.
- `result.getOrElse(() => defaultValue)`: Returns the validated data if `isOk` is true, otherwise calls the fallback function.

```dart
import 'package:ack/ack.dart';

final schema = Ack.string().minLength(5);
final result = schema.safeParse('abc');

if (result.isFail) {
  final error = result.getError();
  print('Validation failed: $error');

  // Try to get data (will throw)
  try {
    result.getOrThrow();
  } catch (e) {
    print('Caught exception: $e');
  }

  // Get a default value
  final dataOrDefault = result.getOrElse(() => 'default_string');
  print('Data or default: $dataOrDefault'); // Output: default_string
}
```

## Understanding `SchemaError`

The `SchemaError` object contains details about the validation failure.

- `error.name`: The schema/context name where validation failed (for example, a field name or your `debugName`).
- `error.value`: The actual value that failed validation.
- `error.schema`: The schema that was being validated against.
- `error.path`: The JSON Pointer path to the failing location (for example, `#/address/city`).

```dart
final userSchema = Ack.object({
  'name': Ack.string(),
  'age': Ack.integer().min(18),
  'address': Ack.object({
    'city': Ack.string()
  })
});

final invalidData = {
  'name': 'Test',
  'age': 15, // Fails min(18)
  'address': {
    'city': 123 // Fails Ack.string
  }
};

final result = userSchema.safeParse(invalidData);

if (result.isFail) {
  final error = result.getError();
  print('Error Name: ${error.name}');         // Example: 'address'
  print('Error Path: ${error.path}');         // Example: '#/address/city'
  print('Failed Value: ${error.value}');      // Example: the invalid data
  print('Full Error: $error');               // Complete error details

  // Note: The exact error structure (especially for nested errors) can vary.
  // Sometimes the top-level error might be SchemaNestedError,
  // and you might need to inspect its nested errors.
}
```

## Error Types

Ack uses specific error types that inherit from `SchemaError`. Each error type provides detailed information about why validation failed.

### `TypeMismatchError`

Occurs when the input value type doesn't match the expected schema type.

```dart
final schema = Ack.string();
final result = schema.safeParse(42); // Number instead of string

if (result.isFail) {
  final error = result.getError() as TypeMismatchError;
  print('Expected: ${error.expectedType}'); // "string"
  print('Actual: ${error.actualType}');     // "integer"
  print('Error: ${error.message}');          // "Expected string, got integer"
}
```

### `SchemaConstraintsError`

Occurs when the value violates one or more validation constraints (e.g., `minLength`, `min`, `email`).

```dart
final schema = Ack.string().minLength(5).email();
final result = schema.safeParse('abc');

if (result.isFail) {
  final error = result.getError() as SchemaConstraintsError;
  print('Constraint violations: ${error.constraints.length}');

  // Access individual failed constraints
  for (final constraintError in error.constraints) {
    print('- Constraint: ${constraintError.constraint}');
    print('- Message: ${constraintError.message}');
  }
}
```

### `SchemaNestedError`

Occurs when validation fails within nested objects or lists. Contains a list of child errors.

```dart
final schema = Ack.object({
  'name': Ack.string().minLength(2),
  'age': Ack.integer().min(0),
});

final result = schema.safeParse({
  'name': 'J',    // Too short
  'age': -5,      // Negative
});

if (result.isFail) {
  final error = result.getError() as SchemaNestedError;
  print('Nested errors: ${error.errors.length}');

  // Recursively inspect nested errors
  for (final nestedError in error.errors) {
    print('- Field: ${nestedError.name}');
    print('- Error: ${nestedError.message}');
  }
}
```

### `SchemaValidationError`

Occurs when custom validation logic added via `.refine()` fails.

```dart
final schema = Ack.integer().refine(
  (value) => value % 2 == 0,
  message: 'Must be an even number',
);

final result = schema.safeParse(3);

if (result.isFail) {
  final error = result.getError() as SchemaValidationError;
  print('Validation error: ${error.message}'); // "Must be an even number"
}
```

### `SchemaTransformError`

Occurs when a transformation function (from `.transform()`) throws an exception.

Transform callbacks receive the non-null validated runtime value; null handling is owned by the surrounding schema's `nullable`/`withDefault` configuration. A `SchemaTransformError` is raised when the callback itself throws — for example, when the input shape passes validation but the conversion fails:

```dart
final schema = Ack.string().transform((value) {
  final parsed = int.tryParse(value);
  if (parsed == null) throw FormatException('Not numeric: $value');
  return parsed;
});

final result = schema.safeParse('not-a-number');

if (result.isFail) {
  final error = result.getError() as SchemaTransformError;
  print('Transform error: ${error.message}');
  print('Cause: ${error.cause}'); // Original exception
}
```

### Working with Error Types

Use pattern matching or type checks to handle different error types:

```dart
final result = schema.safeParse(data);

if (result.isFail) {
  final error = result.getError();

  switch (error) {
    case TypeMismatchError():
      print('Wrong type: expected ${error.expectedType}');
    case SchemaConstraintsError():
      print('Failed ${error.constraints.length} constraints');
    case SchemaNestedError():
      print('Nested validation failed at ${error.errors.length} locations');
    case SchemaValidationError():
      print('Custom validation failed: ${error.message}');
    case SchemaTransformError():
      print('Transformation failed: ${error.cause}');
    default:
      print('Validation failed: ${error.message}');
  }
}
```

## Displaying Errors in UI (Flutter Example)

When using Ack with Flutter forms, you can extract the error message for display.

```dart
// Inside TextFormField validator
validator: (value) {
  final result = someSchema.safeParse(value);
  if (result.isFail) {
    // Return the error details for the UI
    return result.getError().toString();
  }
  return null; // Return null if valid
}
```

*See the [Form Validation Guide](../guides/flutter-form-validation.mdx) for more details.*

## Custom Error Messages

Built-in constraints provide default error messages. For custom error messages, create custom constraints using the `.constrain()` method. See [Custom Validation Guide](../guides/custom-validation.mdx) for details.

```dart
// Built-in constraints use default messages
final schema = Ack.string().minLength(5);

final result = schema.safeParse('abc');
if (result.isFail) {
  final error = result.getError();
  print(error.toString()); // Output: validation error details
}
```

## Next Steps

Learn more about related topics:

- **[Custom Validation](/guides/custom-validation)**: Create custom constraints with tailored error messages
- **[Flutter Forms](/guides/flutter-form-validation)**: Display validation errors in Flutter form widgets
- **[Validation Rules](/core-concepts/validation)**: Explore all built-in validation constraints
- **[Schema Types](/core-concepts/schemas)**: Learn about different schema types and their behavior
- **[Common Recipes](/guides/common-recipes)**: See practical error handling patterns
