ErrLookupBackground articles › "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them

"environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them

"environment variable is not set", "Missing keys=['...'] in environment.", "<VAR> is required for ..." are the messages of the missing-env-var family: errors a library or CLI raises when a required environment variable is absent, empty, or whitespace-only at the moment the code reads it. A developer meets this family when a process starts or a client is constructed under Docker, systemd, cron, CI, or a GUI launch that did not inherit an exported variable, when only part of a required group is configured, or when a value is set but empty, mis-spelled, or read at the wrong time. ErrLookup documents 166 records of this family across 28 repositories, from LiteLLM telemetry callbacks and AnythingLLM provider classes to deletion CLIs that refuse to run without full configuration.

Distilled from 166 documented records across 28 repositories.

Background

These errors come from the library's own configuration validation, not from the operating system: the environment itself works, but the code's expectation that a named variable holds a usable value failed. The check runs in several places across the family: constructors (LiteLLM's OpenMeter, Arize, PostHog and Levo callbacks validate in __init__; AnythingLLM and Chroma provider classes throw in constructors), CLI preflight (buzz-deletion's required_env() refuses to start, buzz-admin checks BUZZ_RELAY_PRIVATE_KEY before member commands, the ECC ito wrapper checks ECC_ITO_CLI_EXECUTABLE before spawning anything), factory and lazy resolution (nautilus_trader's Credential::resolve, CodeWhale's FleetAlertSecretResolver at dispatch, GitButler's OpenAiProvider when client() is first built), and endpoint guards (LiteLLM's STORE_MODEL_IN_DB gate returns HTTP 500). The shared design intent is fail fast: report misconfiguration before any work starts.

From the caller's side the family varies along two axes: when validation runs and how severe it is. Constructor-time checks crash at startup or first import, so adding 'openmeter' to a callbacks list without OPENMETER_API_KEY breaks callback initialization, and constructing a Turso Database with remoteWritesExperimental and a lazy url provider that returns null throws at open time. Per-request checks surface as failed requests instead of startup crashes: AnythingLLM instantiates a provider class on selection, so a missing FIREWORKS_AI_LLM_API_KEY or ANTHROPIC_API_KEY fails the first chat or embedding request. Some members are warnings that proceed: turbo prints "finished with warnings" and lists each missing variable with its task id while the run completes, and claude-mem skips server key bootstrap with a warning, falling back to the worker path. Nearly every message names the offending variable, which is the single most useful property of the family.

What counts as "missing" is library-specific. LiteLLM's PostHog check tests only for None, so an empty string passes init and fails later at send time; buzz-deletion and ECC trim values and reject whitespace-only ones; caveman also rejects keys containing line breaks because the value is delivered as an HTTP header and a newline would enable header injection. Fallback chains complicate diagnosis: Chroma accepts an api_key argument or an environment variable and auto-detects vendor names like CLOUDFLARE_API_KEY or COHERE_API_KEY over the CHROMA_* defaults, LiteLLM's salt key falls back to the master key, and GitButler tries its secret store before OPENAI_API_KEY in the environment. Groups and pairs fail when half-configured: nautilus_trader resolves credentials only when a complete AX_API_KEY/AX_API_SECRET pair exists, and the Levo callback requires all four LEVOAI_* values.

The dominant real-world trigger is environment context mismatch: a variable exported in an interactive shell is invisible to systemd, launchd, cron, Docker containers, CI jobs, and GUI-launched desktop apps, a point the GitButler, CodeWhale, buzz-admin and LiteLLM records all make explicitly. Name resolution adds its own traps: Windows environment lookups are case-insensitive while env::vars returns stored casing, so rustfs's prefix scan misses a lowercase rustfs_policy_plugin_url, and AnythingLLM matches LLM_PROVIDER values case-sensitively in a switch. Some tools refuse to guess on purpose: buzz-deletion ships no localhost defaults because it hard-deletes data across PostgreSQL, S3 and Redis, and buzz-admin will not generate an ephemeral signing key because clients verify the relay's known pubkey.

Common causes

What usually fixes it

Documented occurrences

…and 146 more across the corpus — use search.

Honest provenance: generated on 2026-08-19 from AI-assisted analysis of the linked records. See how records are made.