{"record":{"id":"d12df42c0675a90d","repo":"headroomlabs-ai/headroom","slug":"deployment-env-missing-self-config-env-var","errorCode":null,"errorMessage":"deployment env missing {self.config_env_var}","messagePattern":"deployment env missing (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"headroom/testing/harness.py","lineNumber":395,"sourceCode":"    command: tuple[str, ...]\n    env: dict[str, str]\n    config_payload: dict[str, Any]\n    config_env_var: str = \"HEADROOM_PROXY_CONFIG_JSON\"\n\n    def to_dict(self) -> dict[str, Any]:\n        return {\n            \"command\": list(self.command),\n            \"env\": dict(self.env),\n            \"config_payload\": dict(self.config_payload),\n            \"config_env_var\": self.config_env_var,\n        }\n\n    def validate(self) -> None:\n        \"\"\"Fail if the environment does not round-trip the full config payload.\"\"\"\n\n        raw = self.env.get(self.config_env_var)\n        if raw is None:\n            raise ValueError(f\"deployment env missing {self.config_env_var}\")\n        parsed = json.loads(raw)\n        if parsed != self.config_payload:\n            raise ValueError(f\"{self.config_env_var} does not match config_payload\")\n\n\ndef _field_contract(\n    owner: Literal[\"headroom\", \"proxy\"], cls: type[Any]\n) -> tuple[FieldContract, ...]:\n    if not is_dataclass(cls):\n        raise TypeError(f\"{cls!r} must be a dataclass\")\n    out: list[FieldContract] = []\n    for field in cls.__dataclass_fields__.values():\n        default: Any = MISSING\n        if field.default is not MISSING:\n            default = field.default\n        elif field.default_factory is not MISSING:  # type: ignore[attr-defined]\n            default = \"<factory>\"\n        out.append(","sourceCodeStart":377,"sourceCodeEnd":413,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/testing/harness.py#L377-L413","documentation":"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.","triggerScenarios":"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.","commonSituations":"Test harnesses that copy a base environment and filter variables; subprocess wrappers applying an env allowlist; CI runners with restricted env propagation.","solutions":["Ensure the config var is present: spec.env[spec.config_env_var] = json.dumps(spec.config_payload).","If you filtered/copied env, re-add the config var afterwards or stop filtering HEADROOM_* variables.","Run spec.validate() early (right after building the spec) to catch this before launching the deployment."],"exampleFix":"# before\nspec = DeploymentSpec(env={**os.environ}, ...)  # HEADROOM_PROXY_CONFIG_JSON dropped\nspec.validate()  # ValueError\n\n# after\nspec.env['HEADROOM_PROXY_CONFIG_JSON'] = json.dumps(spec.config_payload)\nspec.validate()","handlingStrategy":"validation","validationCode":"def env_carries_config(spec) -> bool:\n    return spec.config_env_var in spec.env and spec.env[spec.config_env_var] is not None","typeGuard":null,"tryCatchPattern":"try:\n    spec.validate()\nexcept ValueError as e:\n    if 'missing' in str(e):\n        spec.env[spec.config_env_var] = json.dumps(spec.config_payload)\n        spec.validate()\n    else:\n        raise","preventionTips":["Call validate() before spawning the child process.","Treat the config env var as mandatory in env sanitizers."],"tags":["testing","deployment","environment"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}