headroomlabs-ai/headroom · error · ValueError

{self.config_env_var} does not match config_payload

Error message

{self.config_env_var} does not match config_payload

What it means

ValueError raised by DeploymentSpec.validate() in headroom/testing/harness.py when the config env var IS present but its JSON content does not equal spec.config_payload (json.loads(raw) != config_payload). This catches drift between the payload the test believes it deployed and what the environment actually carries — e.g. an older serialization, partial update, or different key ordering is fine (dict equality) but different values/keys are not.

Source

Thrown at headroom/testing/harness.py:398

    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(
            FieldContract(
                owner=owner,
                name=field.name,

View on GitHub (pinned to 322425c43b)

Solutions

  1. Regenerate the env var from the payload: spec.env[spec.config_env_var] = json.dumps(spec.config_payload).
  2. If you must mutate config_payload, re-serialize immediately afterwards.
  3. Prefer the harness's own builder (to_dict/from_dict) over hand-assembling the env var.

Example fix

# before
spec.config_payload['mode'] = 'cache'   # mutated after injection
spec.validate()  # ValueError: does not match

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

Strategy: validation

Validate before calling

import json

def env_matches_payload(spec) -> bool:
    raw = spec.env.get(spec.config_env_var)
    if raw is None:
        return False
    try:
        return json.loads(raw) == spec.config_payload
    except json.JSONDecodeError:
        return False

Try / catch

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

Prevention

When it happens

Trigger: Setting spec.env['HEADROOM_PROXY_CONFIG_JSON'] to a hand-written or stale JSON string instead of json.dumps(spec.config_payload); mutating spec.config_payload after injecting the env var; double-encoding (dumps of an already-JSON string).

Common situations: Tests that tweak config fields after building the deployment; fixtures caching serialized configs; copy-pasting an env var from a previous run's log.

Related errors


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