envil envil Docs

Error Handling

Understand the error classes thrown by envil and how to handle them.

envil uses three distinct error types to signal different failure modes. All error classes are exported from the main package.

import { EnvValidationError, ClientAccessError } from "@ayronforge/envil"

EnvValidationError

Thrown when one or more environment variables fail schema validation.

Name Type Default Description
_tag "EnvValidationError" Discriminant tag for pattern matching.
errors ReadonlyArray<string> Array of human-readable validation error messages.
message string Formatted error message with all failures.

Example

import { createEnv, requiredString, port, EnvValidationError } from "@ayronforge/envil"

try {
  const env = createEnv({
    server: {
      DATABASE_URL: requiredString,
      PORT: port,
    },
  })
} catch (e) {
  if (e instanceof EnvValidationError) {
    console.error("Validation failed:")
    for (const error of e.errors) {
      console.error(`  - ${error}`)
    }
    // Output:
    //   - DATABASE_URL: Expected a string with a length of at least 1, but got undefined
    //   - PORT: Expected Port (1-65535), but got "abc"
  }
}
Note

EnvValidationError collects all validation failures, not just the first one. This lets you fix all issues in a single pass rather than playing whack-a-mole.

onValidationError callback

You can hook into validation errors before the exception is thrown:

createEnv({
  onValidationError: (errors) => {
    // errors is string[] — same as EnvValidationError.errors
    logger.error("Environment validation failed", { errors })
  },
  server: {
    DATABASE_URL: requiredString,
  },
})

The callback fires before the error is thrown. The EnvValidationError is still thrown after the callback completes.

ClientAccessError

Thrown when client-side code attempts to access a server-only environment variable.

Name Type Default Description
_tag "ClientAccessError" Discriminant tag for pattern matching.
variableName string The name of the server variable that was accessed.
message string Descriptive error message.

Example

import { createEnv, requiredString, ClientAccessError } from "@ayronforge/envil"

const env = createEnv({
  server: {
    DATABASE_URL: requiredString,
  },
  client: {
    NEXT_PUBLIC_API_URL: requiredString,
  },
  isServer: false, // simulate client-side
})

try {
  env.DATABASE_URL // throws on client
} catch (e) {
  if (e instanceof ClientAccessError) {
    console.error(e.variableName) // "DATABASE_URL"
    // "Attempted to access server-side env var "DATABASE_URL" on client"
  }
}

ResolverError

Thrown when a resolver fails to initialize or fetch secrets. This is an Effect tagged error, used in the Effect error channel.

Name Type Default Description
_tag "ResolverError" Discriminant tag for Effect error handling.
resolver string Name of the resolver that failed (e.g., "aws", "gcp").
message string Human-readable error message.
cause unknown The underlying error, if any.

ResolverError is a Data.TaggedError from Effect, meaning you can use it with Effect’s error handling:

import { Effect } from "effect"
import { createEnv, requiredString } from "@ayronforge/envil"
import { fromAwsSecrets, ResolverError } from "@ayronforge/envil/aws"

const envEffect = createEnv({
  server: {
    DATABASE_URL: requiredString,
  },
  resolvers: [
    fromAwsSecrets({
      secrets: { DATABASE_URL: "prod/db-url" },
    }),
  ],
})

// Handle resolver errors specifically
const program = envEffect.pipe(
  Effect.catchTag("ResolverError", (err) => {
    console.error(`Resolver "${err.resolver}" failed: ${err.message}`)
    return Effect.fail(err)
  }),
)
Warning

When using resolvers, createEnv returns an Effect.Effect instead of a plain object. You must run it with Effect.runPromise or Effect.runSync (or use it within an Effect pipeline).

Safe alternative

If you prefer handling errors without try/catch or Effect error channels, use safeCreateEnv. It captures errors in a discriminated result object instead of raising exceptions:

import { safeCreateEnv, requiredString } from "@ayronforge/envil"

const result = safeCreateEnv({
  server: { DATABASE_URL: requiredString },
  isServer: true,
})

if (!result.success) {
  // result.error is EnvValidationError
  console.error(result.error.errors)
}
Tip

With resolvers, safeCreateEnv returns an Effect that never fails — both ResolverError and EnvValidationError are captured in the result object. See Safe Parsing for details.