headroomlabs-ai/headroom · error · ValueError

deployment env missing {self.config_env_var}

Error message

deployment env missing {self.config_env_var}

What it means

ValueError raised by DeploymentSpec.validate() in headroom/testing/harness.py when the deployment environment dict (self.env) does not contain the config env var (self.config_env_var, default 'HEADROOM_PROXY_CONFIG_JSON'). validate() exists to prove the environment round-trips the full config payload — the child process is expected to receive its config through that single env var — so a missing var means the deployment is not wired correctly.

Source

Thrown at headroom/testing/harness.py:395

    command: tuple[str, ...]
    env: dict[str, str]
    config_payload: dict[str, Any]
    config_env_var: str = "HEADROOM_PROXY_CONFIG_JSON"

    def to_dict(self) -> dict[str, Any]:
        return {
            "command": list(self.command),
            "env": dict(self.env),
            "config_payload": dict(self.config_payload),
            "config_env_var": self.config_env_var,
        }

    def validate(self) -> None:
        """Fail if the environment does not round-trip the full config payload."""

        raw = self.env.get(self.config_env_var)
        if raw is None:
            raise ValueError(f"deployment env missing {self.config_env_var}")
        parsed = json.loads(raw)
        if parsed != self.config_payload:
            raise ValueError(f"{self.config_env_var} does not match config_payload")


def _field_contract(
    owner: Literal["headroom", "proxy"], cls: type[Any]
) -> tuple[FieldContract, ...]:
    if not is_dataclass(cls):
        raise TypeError(f"{cls!r} must be a dataclass")
    out: list[FieldContract] = []
    for field in cls.__dataclass_fields__.values():
        default: Any = MISSING
        if field.default is not MISSING:
            default = field.default
        elif field.default_factory is not MISSING:  # type: ignore[attr-defined]
            default = "<factory>"
        out.append(

View on GitHub (pinned to 322425c43b)

Solutions

  1. Ensure the config var is present: spec.env[spec.config_env_var] = json.dumps(spec.config_payload).
  2. If you filtered/copied env, re-add the config var afterwards or stop filtering HEADROOM_* variables.
  3. Run spec.validate() early (right after building the spec) to catch this before launching the deployment.

Example fix

# before
spec = DeploymentSpec(env={**os.environ}, ...)  # HEADROOM_PROXY_CONFIG_JSON dropped
spec.validate()  # ValueError

# after
spec.env['HEADROOM_PROXY_CONFIG_JSON'] = json.dumps(spec.config_payload)
spec.validate()
Defensive patterns

Strategy: validation

Validate before calling

def env_carries_config(spec) -> bool:
    return spec.config_env_var in spec.env and spec.env[spec.config_env_var] is not None

Try / catch

try:
    spec.validate()
except ValueError as e:
    if 'missing' in str(e):
        spec.env[spec.config_env_var] = json.dumps(spec.config_payload)
        spec.validate()
    else:
        raise

Prevention

When it happens

Trigger: Building a DeploymentSpec with env={'FOO': '1'} but never injecting the config var; stripping env vars with a sanitizer/filter that drops HEADROOM_*; serializing the spec to JSON (to_dict) and reconstructing it without restoring the config env var.

Common situations: Test harnesses that copy a base environment and filter variables; subprocess wrappers applying an env allowlist; CI runners with restricted env propagation.

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/d12df42c0675a90d. Report an issue: GitHub.