ErrLookup › Background articles › "is required", "must be set", "missing required field": configuration validation errors across open-source libraries
"is required", "must be set", "missing required field": configuration validation errors across open-source libraries
"is required" and "must have" config errors appear when a library validates its configuration at load or startup time and a mandatory field is absent or empty — from litellm guardrails missing a guardrail_name, to Vertex providers without a project_id, mise bootstrap files with no content, and Cilium BGP instances without a localASN. This article explains why these fail-fast checks exist, where they fire, and the general patterns for fixing them.
Distilled from 101 documented records across 36 repositories.
Background
This family covers errors thrown during configuration validation: a library inspects a settings object — a YAML/JSON/TOML file, environment variables, a code-level config struct, or an API request body — and refuses to proceed because a field it considers mandatory is missing, null, empty, or whitespace-only. Unlike runtime failures, these errors usually fire before any real work happens: at startup, at config parse time, or in a validate() call. litellm aborts proxy startup when a guardrail entry lacks guardrail_name; gradle rejects an empty trusted-artifact entry while building verification metadata; mise rejects a bootstrap file entry during from_toml; caveman's objectstore.validateConfig runs inside FromEnv. The validation is deliberate: the library could theoretically continue (and fail later, less clearly), but instead it fails fast with a message naming the offending field.
Why the fields are mandatory varies. Some are needed to construct a value: the Vertex partner endpoint URL embeds the GCP project ID in its path, so 9router's VertexExecutor cannot build a URL at all without one. Some are needed for identity and routing: litellm's guardrail_name is the key used for mode matching, request-level guardrail selection, and logging, so a nameless guardrail cannot be registered. Some are needed to deserialize anything: Cilium's kvstore requires a KeyCreator function to instantiate typed keys during watch operations, and chroma's buildFromConfig cannot reconstruct a Qwen embedding function without model and task. Some simply guard against useless no-ops: zeroclaw's MQTT channel with zero topics would connect and receive nothing, and an empty Gradle trusted-artifact entry would silently disable dependency verification for everything.
From the caller's side, the error usually appears immediately after a config change — adding a new receiver, guardrail, provider, or channel — or after an upgrade, when a stored or generated config predates a required field (chroma collections persisted by older versions, mastra registries saved before servers_url existed). The messages are typically literal: fluentd names the missing key (host and port), mise lists the exact scheduling keys a timer unit must set, oh-my-pi enumerates every acceptable field an empty provider stanza could have set. Most libraries include the offending value's name — provider, model, instance, or guardrail — in the message.
Across the 36 repositories the family varies mainly in strictness and indirection. Some validators check only shape (caveman validates endpoint and bucket are non-empty after trimming; gradle needs one of four filter attributes, not all). Others cross-reference fields: oh-my-pi accepts api at either the provider or model level and exempts proxy-type discovery; 9router tries automatic project-ID resolution from the API key before giving up; mise reclassifies entries (a systemd entry with timer metadata but no schedule is rejected, while the same keys without any timer metadata would classify as a plain service). Defaults interact with validation in surprising ways: mise file entries default to state "present" and therefore require a body even when the author only wrote metadata keys; zeroclaw's topics key has an empty-vec default, so omitting it fails validation rather than silently subscribing to nothing. Whether whitespace-only values count as missing is also library-specific — caveman trims and rejects them, and crush rejects SSE URLs that resolve via shell variables to empty strings.
Common causes
- Field placed at the wrong nesting level. A required key is written inside a sub-object instead of beside it — litellm guardrails put guardrail_name inside litellm_params rather than as a sibling of it, and YAML indentation in Signoz receiver configs can leave webhook_url unparsed. The validator reads the top level, gets None, and raises even though the value exists somewhere in the file.
- Key omitted entirely when adding a new entry. A new guardrail, receiver, provider, channel, or benchmark entry is added with only the 'interesting' fields and the mandatory identifier or endpoint is forgotten — a googlechat receiver without webhook_url, an MCP SSE server without url, a models-config provider stanza with only a name.
- Environment variable or templated value resolves to empty. The config references an env var or shell expression ($MCP_URL, an env-substituted webhook_url) that is unset in the deployment environment, so the field is present in the file but empty after resolution. Crush explicitly catches SSE URLs that expand to nothing.
- Defaults that silently imply a requirement. Omitting a key selects a default that carries its own requirement: mise file entries default to state "present" and therefore need source or content; persistent = true reclassifies a systemd entry as a timer, which must then declare a schedule; zeroclaw's topics defaults to an empty vec that fails validation.
- Configs persisted or generated before the field existed. Records written by an older library version lack a newly required field — chroma embedding-function configs without model/task, mastra registries stored before servers_url — and fail when rebuilt. Hand-rolled generators can also emit empty entries, like gradle's all-null trusted-artifact.
- Credential shape lacking an embedded identifier. 9router's Vertex support needs a GCP project ID that a raw API key or bare accessToken does not carry; only SA JSON (project_id) and ADC JSON (quota_project_id) embed it, so raw-key connections require providerSpecificData.projectId.
- Explicitly emptied lists or placeholder stanzas left behind. zeroclaw's cloud_ops enabled with supported_clouds = [], an MQTT channel with topics = [], or a leftover empty provider block in oh-my-pi: the entry exists and is enabled but carries no usable content, so validation rejects it rather than doing nothing.
What usually fixes it
- Set the named field at the level the validator reads it: most of these errors are literal about which key is missing — add guardrail_name beside litellm_params, webhook_url on the receiver, api on the provider or each model, data_dir on the dataset block.
- Prefer provider- or parent-level values that children inherit over per-entry repetition: oh-my-pi models inherit a provider-level api, and template-based configs (copy a known-good guardrail or receiver entry) reduce missed required fields.
- Verify that env vars and shell expressions used in config values actually expand to non-empty values in the launch environment; if the field comes from the environment, check the deployment, not just the file.
- When narrowing or decommissioning, edit contents rather than emptying or omitting keys: set state = "absent", remove timer metadata to reclassify as a service, or delete placeholder stanzas entirely.
- Validate early and automatically: dry-run modes (fluentd --dry-run, mise bootstrap plan), schema checks in CI on YAML/TOML/JSON, and config constructors or builders that populate required fields together prevent these failures from reaching deploy time.
- After library upgrades, validate or migrate stored configs and persisted records before use — rebuild embedding-function configs via getConfig(), re-create registries missing newer fields, and backfill required keys.
Documented occurrences
- Provider ${providerName}, model ${modelDef.id}: no "api" specified. / Provider ${providerName}, model ${modelDef.id}: no "api" specified. Set at provider or model level. (can1357/oh-my-pi)
- Vertex partner models require a project_id. Add it in providerSpecificData or use Service Account JSON. (decolua/9router)
- path dataset requires data_dir (zed-industries/zed)
- Block Code Execution guardrail requires a guardrail_name (BerriAI/litellm)
- Vertex OAuth/ADC requires a project_id. Add quota_project_id to your ADC JSON or set providerSpecificData.projectId. (decolua/9router)
- Provider ${providerName}: must specify "baseUrl", "headers", "apiKey", "auth: none", "compat", "disableStrictTools", "guardrailIdentifier", "remoteCompaction", "modelOverrides", "discovery", or "models" (can1357/oh-my-pi)
- writer ${worker.id} requires an isolated worktree (ruvnet/ruflo)
- Expected a route.id in react-router processRoutes() function (remix-run/react-router)
- Vertex: could not resolve project_id from API key. Please add it manually in provider settings. (decolua/9router)
- A trusted artifact must have at least one of group, name, version or file name not null (gradle/gradle)
- CrowdStrike AIDR guardrail name is required (BerriAI/litellm)
- AWS service name must not be empty (openai/codex)
- Provider ${providerName}: "api" is required when discovery is enabled at provider level. (can1357/oh-my-pi)
- Config is missing a required field (chroma-core/chroma)
- #{e}. Service must have `host` and `port` (fluent/fluentd)
- [bootstrap.files]."{}": present files require source or content (jdx/mise)
- failed to get local ASN for instance %v: %w (cilium/cilium)
- Either `dataset_name` or `dataset_mixture` must be provided (huggingface/open-r1)
- KeyCreator must be specified (cilium/cilium)
- failed to get router ID for instance %v: %w (cilium/cilium)
…and 81 more across the corpus — use search.
Honest provenance: generated on 2026-08-31 from AI-assisted analysis of the linked records. See how records are made.