Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/prisma/prisma-next/llms.txt

Use this file to discover all available pages before exploring further.

Prisma Next uses a structured error model across its packages and planes (CLI, migration planning, query planning, runtime execution). The goal is consistent, composable error handling: failures are actionable and machine-readable; bugs fail fast with full stack traces; boundaries convert structured failures into typed Result envelopes that callers can handle deterministically.

The three error categories

A failure is an expected, explainable outcome where a logical condition is not met. The caller can fix something deterministically.Examples:
  • Invalid user input or config shape
  • Plan-builder misuse — for example, calling update() without a .where() clause
  • Capability gating — a feature requires a contract capability that is absent
  • Policy or guardrail blocks — budgets or lints in strict mode
Failures should carry a stable code and structured metadata when surfaced. Within package internals you may throw a structured failure to abort quickly; at system boundaries, convert it to a Result or structured envelope.
An operational error is an expected error caused by an external system, not a bug in Prisma Next.Examples:
  • Database connection refused or timed out
  • Network interruption
  • Postgres driver errors (SQLSTATE codes)
  • Permission or auth failures
Operational errors are often transient. Drivers and adapters typically throw; catch at the boundary when you can translate to a stable, actionable envelope, and retain the original stack where available.
A bug is an invariant break or programming error where the system cannot reliably continue.Examples:
  • Unexpected undefined in a branch that should never be reached
  • Internal assertion failure
  • Serialization or type invariants broken after validation
Bugs should throw and fail fast. Catch only at the outermost boundary for crash reporting or last-resort formatting — do not disguise a bug as an expected failure.

The Result<T, F> type

For expected failures at system boundaries, Prisma Next uses a generic Result<T, F> type from @prisma-next/utils/result. It has two variants: Ok<T> (success with a value) and NotOk<F> (failure with structured data).
import type { Result, Ok, NotOk } from '@prisma-next/utils/result';
import { ok, notOk, okVoid } from '@prisma-next/utils/result';

// Success with a value
function divide(a: number, b: number): Result<number, { code: string; message: string }> {
  if (b === 0) {
    return notOk({ code: 'DIVISION_BY_ZERO', message: 'Cannot divide by zero' });
  }
  return ok(a / b);
}

// Validation that returns void on success
function validateInput(input: string): Result<void, { code: string; message: string }> {
  if (input.length === 0) {
    return notOk({ code: 'EMPTY_INPUT', message: 'Input cannot be empty' });
  }
  return okVoid();
}

// Consuming a Result
const result = divide(10, 2);
if (result.ok) {
  console.log('Result:', result.value);
} else {
  console.log('Error:', result.failure.code);
}
The discriminator property is ok: true | false. Use result.value for the success case and result.failure for the failure case — distinct names prevent confusion between success data and failure data. Both type parameters are required. F has no default because the whole point of Result is to make failure types explicit, not to propagate JavaScript’s untyped error handling.
Result is a boundary policy, not a universal implementation rule. Within package internals, prefer ergonomic throws and catch at the boundary. Use Result at system boundaries (CLI commands, migration runner entrypoints, public SDK entrypoints) where callers — including agents and CI pipelines — need to handle failures deterministically.

When to use Result and when to throw

SituationPattern
Expected failure at a system boundary (CLI, SDK, migration runner)Return Result<T, F>
Expected failure deep inside package internalsThrow a structured error; catch at boundary
Unexpected invariant break or programming errorThrow and fail fast
Streaming API failureAsyncIterable that throws on error

Stable error codes

Expected failures use stable, structured codes so that agents and CI pipelines can match and branch on them without fragile string matching.
NamespaceExamplesUsed in
PN-CLI-4xxxPN-CLI-4010, PN-CLI-4005, PN-CLI-4020CLI commands
PN-RUN-3xxxPN-RUN-3001, PN-RUN-3002, PN-RUN-3003Runtime / contract marker verification
PLAN.*PLAN.INVALID, PLAN.UNSUPPORTEDSQL plan builder
BUDGET.*Guardrail blocks in strict modeRuntime middleware
PN-MIG-2001Unfilled placeholder() slotMigration self-emit
MIGRATION.*MIGRATION.HASH_MISMATCHMigration loader / apply
Example error output with a stable code:
{
  "code": "PN-RUN-3001",
  "message": "Contract marker not found in database",
  "fix": "Run `prisma-next db sign --db <url>` to create the marker"
}

CLI error codes and exit codes

The CLI exits with one of three codes:
Exit codeMeaning
0Success
1Runtime error — a structured failure or operational error
2Usage or config error — bad flags, missing required options
PN-CLI-4xxx codes are the primary error codes for CLI command failures. A selection of common ones:
CodeMeaning
PN-CLI-4004Contract file not found
PN-CLI-4005Missing database connection — provide --db or set db.connection
PN-CLI-4010Missing driver in config
PN-CLI-4012Conflicting flags (e.g., --marker-only with --schema-only)
PN-CLI-4020Migration planning failed — conflicts detected
PN-CLI-4021Target does not support migrations

Runtime streaming errors

Runtime execution returns an AsyncIterable<Row> that throws on error. This shape allows early abort and preserves context and stack in async workflows:
try {
  for await (const row of runtime.execute(plan)) {
    process(row);
  }
} catch (err) {
  // err is a structured RuntimeError with a stable code
  console.error(err.code, err.message);
}
Guardrails (budgets and lints) may block execution by throwing a structured runtime error in strict mode before the first row is yielded. The throw always propagates through the for await loop.

Migration runner error codes

The migration runner returns Result<MigrationRunnerSuccessValue, MigrationRunnerFailure> at its entrypoint. The MigrationRunnerFailure carries a stable MigrationRunnerErrorCode:
CodeMeaning
EXECUTION_FAILEDA migration step threw during apply
SCHEMA_VERIFY_FAILEDSchema verification after apply did not match the expected destination
PRECHECK_FAILEDA pre-execution check did not pass
POSTCHECK_FAILEDA post-execution check did not pass
These codes are defined in @prisma-next/migration-tools and can be matched deterministically in deploy pipelines:
const result = await runner.execute(plan);
if (!result.ok) {
  switch (result.failure.code) {
    case 'EXECUTION_FAILED':
      console.error('Migration step failed:', result.failure.detail);
      break;
    case 'SCHEMA_VERIFY_FAILED':
      console.error('Schema drift after apply:', result.failure.detail);
      break;
    default:
      console.error('Migration failed:', result.failure.code);
  }
  process.exit(1);
}

Build docs developers (and LLMs) love