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; 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
| Code | Name | Thrown when |
|---|---|---|
| 101 | InvalidFallback | A fallback for an array converter isn't an array. |
| 103 | ArrayFallbackElementTypeMismatch | An array fallback mixes element types, or its elements don't match the converter. |
| 104 | FallbackConverterTypeMismatch | The fallback has the wrong type for the converter, such as '80' for Converters.Number in getAs. This throws even when the variable is set. |
| 105 | MalformedTimeFallback | A 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
| Code | Name | Thrown when |
|---|---|---|
| 201 | InvalidArrayConverterType | The Converters.array setup is malformed, such as a schema as its of. |
| 202 | InvalidBuiltInConverter | A converter string isn't a built-in token, like 'nmber'. The message lists the valid tokens. |
| 203 | InvalidCustomConverter | The converter argument has an unsupported type, such as a number. |
| 206 | ArrayElementConversionFailed | An array element can't convert to the element type. |
| 207 | EmptyArrayElement | Strict mode only: an array element is empty or whitespace. |
| 208 | SchemaValidationFailed | A read that isn't required (getAs, or a decorator without required) gets a value its schema rejects. |
| 209 | SchemaThrew | A schema's validate throws during a read that isn't required. The original error is on cause. |
| 210 | InvalidEnvValue | A 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
| Code | Name | Thrown when |
|---|---|---|
| 301 | MissingDelimiter | An array converter's delimiter is empty or isn't a string. |
| 302 | InvalidUserDefinedConfig | A setting, option, or converter is invalid. The message states which. See the list below. |
| 303 | EnvFilesNotFound | A path in envPaths, or in the active environment's profile, doesn't exist. See .env files on Node. |
| 304 | InvalidKeyInput | The key list is empty, or a key isn't a string or is blank. |
| 305 | MissingEnvValue | A required read finds no value. In strict mode, a ${VAR} reference or a resolve slot with no value throws it too. |
| 306 | FileApiUnsupported | A file API from .env files on Node runs against a source with no files. The builds differ, see the list below. |
| 307 | NoSourceBound | A read runs before any source is bound. The portable build starts unbound (Sources), and a decorated field throws it there too (Decorators). |
| 308 | InvalidMergedSource | merge 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, andconfigureProfilesthrow it while a source with no files is bound. - On the portable build, those three,
envFileOptions, andresetPathsthrow it only whenEnvapter.fileApiModeis'throw'.
These cause a 302, and the message states which:
Envapter.environmentset to something that isn't a stringEnvapter.debugset to an unknown levelsyncProcessEnvset to a non-booleanfileApiModeset to anything other than'warn'or'throw'envFileOptionsgiven a key other thanencodingandoverrideString,Number,Boolean, orBigIntpassed as a converter. The message gives the matching member, such asConverters.StringSymbolpassed as a converter- a decorator given
requiredtogether withfallback - a decorator given
schematogether withconverter, aschemathat isn't a Standard Schema, or a schema asconverter - a schema whose
validatereturns a Promise, which zod does when a.transformor.refinethrows - 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.
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.
| Level | Logs |
|---|---|
silent (default) | nothing from reads. The portable build still prints its file-API warnings (Compatibility) |
warn | a .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 |
verbose | everything 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.