v9.1.0

Errors and debugging

Find what an EnvaptError code means, and turn on debug logging to see why a read returned your fallback.

A read threw an EnvaptError with a number in its name? Find it in the codes. A read returned something other than what your .env file says? Turn on debug logging.

Catching an error

When envapt detects a failure, it throws an EnvaptError with a numeric code from EnvaptErrorCodes, so you can branch on the cause.

try {
    ();
} catch () {
    if ( instanceof ) {
        .code;
EnvaptError.code: EnvaptErrorCodes

The EnvaptErrorCodes value identifying what failed.

if (. === .) { .(.); } } throw ; }

error.code is typed EnvaptErrorCodes, so comparing it with a member such as MissingEnvValue narrows it. A MissingEnvValue (305) message lists keys only, so printing it is safe. The instanceof check matters because a converter function you wrote can throw its own error, which has no code. Inside getAs or a decorated field, envapt rethrows that error unchanged. A required read puts its message in the issues of an InvalidEnvValue (210) and keeps your error as that issue's cause.

The error shape

Prop

Type

An issue's message can quote the value it rejected. Reading the error without leaking values shows what to log instead.

Codes

Fallback

CodeNameThrown when
101InvalidFallbackA fallback for an array converter isn't an array.
103ArrayFallbackElementTypeMismatchAn array fallback mixes element types, or its elements don't match the converter.
104FallbackConverterTypeMismatchThe fallback has the wrong type for the converter, such as '80' for Converters.Number in getAs. This throws even when the variable is set.
105MalformedTimeFallbackA time-string fallback isn't <number><unit>, such as '5' when you meant '5m'. Converters lists the units.

Every row here is a mistake in the code that makes the read, found the first time that read runs.

Converter and schema

CodeNameThrown when
201InvalidArrayConverterTypeThe Converters.array setup is malformed, such as a schema as its of.
202InvalidBuiltInConverterA converter string isn't a built-in token, like 'nmber'. The message lists the valid tokens.
203InvalidCustomConverterThe converter argument has an unsupported type, such as a number.
206ArrayElementConversionFailedAn array element can't convert to the element type.
207EmptyArrayElementStrict mode only: an array element is empty or whitespace.
208SchemaValidationFailedA read that isn't required (getAs, or a decorator without required) gets a value its schema rejects.
209SchemaThrewA schema's validate throws during a read that isn't required. The original error is on cause.
210InvalidEnvValueA required read gets a value it can't convert, or one your converter or schema rejects. getRequiredAll throws one 210 that lists every bad value.

The 201, 202, and 203 rows are mistakes in the code. The other rows come from the value in your environment. In a required read, 206, 207, and 209 arrive as issues of a 210.

Configuration and lookup

CodeNameThrown when
301MissingDelimiterAn array converter's delimiter is empty or isn't a string.
302InvalidUserDefinedConfigA setting, option, or converter is invalid. The message states which. See the list below.
303EnvFilesNotFoundA path in envPaths, or in the active environment's profile, doesn't exist. See .env files on Node.
304InvalidKeyInputThe key list is empty, or a key isn't a string or is blank.
305MissingEnvValueA required read finds no value. In strict mode, a ${VAR} reference or a resolve slot with no value throws it too.
306FileApiUnsupportedA file API from .env files on Node runs against a source with no files. The builds differ, see the list below.
307NoSourceBoundA read runs before any source is bound. The portable build starts unbound (Sources), and a decorated field throws it there too (Decorators).
308InvalidMergedSourcemerge gets no members, or more than one source that reads files.

305 is about the value in your environment. The other codes in this table are mistakes in the code or setup.

A 306 comes from different calls on each build:

  • On the node build, envPaths, baseDir, and configureProfiles throw it while a source with no files is bound.
  • On the portable build, those three, envFileOptions, and resetPaths throw it only when Envapter.fileApiMode is 'throw'.

These cause a 302, and the message states which:

  • Envapter.environment set to something that isn't a string
  • Envapter.debug set to an unknown level
  • syncProcessEnv set to a non-boolean
  • fileApiMode set to anything other than 'warn' or 'throw'
  • envFileOptions given a key other than encoding and override
  • String, Number, Boolean, or BigInt passed as a converter. The message gives the matching member, such as Converters.String
  • Symbol passed as a converter
  • a decorator given required together with fallback
  • a decorator given schema together with converter, a schema that isn't a Standard Schema, or a schema as converter
  • a schema whose validate returns a Promise, which zod does when a .transform or .refine throws
  • an assignment to a decorated field (Decorators)
  • the removed positional @Envapt(key, fallback, converter) form
  • a build tool's Stage 3 decorator transform that leaves out the accessor name

Debug logging

By default, if a converter such as Converters.Port can't convert a value, the read returns your fallback and logs nothing. A PORT of 80a reads as 3000, and nothing shows it happened. At the verbose level, debug logging prints a line for each such event, plus the files envapt loaded and the order they loaded in. An array element that can't convert throws ArrayElementConversionFailed (206) instead.

Setting the level

The level is a DebugLevel: silent, warn, or verbose. Set it in the shell to log from the first load, or in code to log events after that line.

ENVAPT_DEBUG=verbose NODE_ENV=production node app.js
. = 'verbose';

On Node, envapt loads the .env files when you import it, so an Envapter.debug assignment in your code runs after that load and logs only later events. An assignment to Envapter.debug wins over ENVAPT_DEBUG whenever it runs.

WARNING

On Node, ENVAPT_DEBUG has to be set before the process starts, in the shell or your process manager. A line in a .env file is ignored, because envapt reads the level before it loads any file. A value other than the three lowercase levels is ignored as well.

If you bind a source yourself, as on Workers or in the browser, ENVAPT_DEBUG comes from that source. On Workers a vars entry in your Wrangler config works, because PortableSource reads it like any other key.

Reading the output

Each line starts with [envapt] and a space. It goes to stderr on Node and to console.error on the portable build. Take an app that reads the port with a fallback.

const  = .('PORT', ., 3000);

With PORT=8080 in .env and PORT=80a in .env.production, a run with ENVAPT_DEBUG=verbose and NODE_ENV=production prints:

[envapt] cache cleared, reloading config
[envapt] base dir: working directory
[envapt] effective .env paths: .env.production, .env
[envapt] loaded .env.production: 1 key
[envapt] .env.production -> PORT
[envapt] loaded .env: 3 keys
[envapt] .env -> DATABASE_URL
[envapt] .env -> CACHE_TTL
[envapt] cache populated: 67 keys total
[envapt] could not convert PORT as port, using the fallback

.env.production -> PORT shows the production file set PORT. No .env -> PORT line follows. envapt keeps the first value it loads for a key, so it skipped the valid PORT=8080 in .env. The last line says why PORT reads as 3000. The key total counts your shell's variables too.

LevelLogs
silent (default)nothing from reads. The portable build still prints its file-API warnings (Compatibility)
warna .env file that couldn't be read, a ${VAR} reference left unresolved, an unset or empty variable read through get, a typed getter, getAs, or a decorated field without required (whether it returns a fallback or undefined), an environment name envapt doesn't recognize or no environment name at all, and the error from a schema that returned a Promise
verboseeverything in warn, plus the base directory, the effective paths, every file loaded with its key count, each key a file sets, cache rebuilds, keys mirrored to process.env, and a present value a converter couldn't convert

Lines carry keys and file paths. They leave values out, except the unrecognized environment name and the error text from a rejected schema. Use verbose for one run while you debug, since it prints a line for every key in every file.

On this page