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"
}
}
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)
}),
)
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)
}
With resolvers, safeCreateEnv returns an Effect that never fails — both ResolverError and EnvValidationError are captured in the result object. See Safe Parsing for details.