CtroEnv
ctroenvType-Safe Environment Variables
Getting StartedQuick StartCore Concepts
defineEnv()string()number()boolean()pick()Chainable MethodsRefinementsError HandlingSchema Composition
CLI Overviewctroenv validatectroenv generatectroenv checkctroenv docsctroenv initCLI Configuration
Node AdapterVite AdapterNext.js Adapter
Migration from t3-envMigration from envalidMigration from dotenv

Error Handling

Handle validation errors with CtroEnvError, formatErrors, and error categories.

  1. Docs
  2. Core API

Error Handling

CtroEnv provides a structured error system with machine-readable codes and human-readable messages with suggestions.

CtroEnvError

When one or more validations fail, defineEnv() throws a CtroEnvError:

class CtroEnvError extends Error {
  readonly errors: readonly ValidationError[]
}

The error message is automatically formatted using formatErrors().

ValidationError

Each individual field error is a ValidationError object:

class ValidationError {
  readonly key: string
  readonly message: string
  readonly code: ErrorCode
  readonly value: unknown | undefined
  readonly suggestion: string | undefined
}
FieldTypeDescription
keystringThe environment variable name
messagestringHuman-readable error description
codeErrorCodeMachine-readable error category
valueunknownThe original value that failed validation
suggestionstring | undefinedA suggested fix (when available)

Error Codes

const ErrorCode = {
  MissingRequired: "missing_required",
  TypeMismatch: "type_mismatch",
  InvalidValue: "invalid_value",
  ValidationFailed: "validation_failed",
} as const

missing_required

A required environment variable was not found in the source.

defineEnv({ DATABASE_URL: string().url() })
// ✗ Missing required environment variable: DATABASE_URL
//   → Add this variable to your .env file or set it in the environment.

type_mismatch

The value exists but has the wrong type (e.g., a string where a number is expected).

defineEnv({ PORT: number().port() })
// With PORT="not-a-number"
// ✗ Expected a number, received "not-a-number"
//   → Ensure the value is a numeric string or number.

invalid_value

The value has the correct type but fails a refinement (e.g., not a valid URL).

defineEnv({ API_URL: string().url() })
// With API_URL="not-a-url"
// ✗ Invalid URL

validation_failed

A custom .validate() function returned an error message.

defineEnv({
  API_KEY: string().validate((v) =>
    v.startsWith("sk_") ? undefined : "Must start with 'sk_'",
  ),
})
// With API_KEY="invalid"
// ✗ Must start with 'sk_'

formatErrors()

Produces a structured, colorized CLI output from an array of errors:

import { formatErrors } from "@ctroenv/core"

try {
  const env = defineEnv(schema)
} catch (e) {
  if (e instanceof CtroEnvError) {
    process.stderr.write(formatErrors(e.errors))
    // ● Missing required (2)
    //   DATABASE_URL
    //     → Add this variable to your .env file or set it in the environment.
    //   JWT_SECRET
    //     → Add this variable to your .env file or set it in the environment.
    //
    // ✗ Invalid (1)
    //   PORT
    //     → Expected a number, received "not-a-number"
    //     → Ensure the value is a numeric string or number.
  }
}

The output is grouped by error category ("Missing required" vs "Invalid") and includes colorized ANSI output when the terminal supports it. Respects NO_COLOR, CI, and TERM=dumb environment variables.

Error Code Type

The ErrorCode constant uses as const, making each value a literal type. Access the union type via ErrorCodeType:

import { ErrorCode, type ErrorCodeType } from "@ctroenv/core"

const code: ErrorCodeType = ErrorCode.MissingRequired
// "missing_required" | "type_mismatch" | "invalid_value" | "validation_failed"

How is this guide?

Edit on GitHub

Last updated on Jun 24, 2026

PreviousRefinementsNextSchema Composition

On this page

CtroEnvErrorValidationErrorError Codesmissing_requiredtype_mismatchinvalid_valuevalidation_failedformatErrors()Error Code Type