{"record":{"id":"bedc8d21177dd4ef","repo":"headroomlabs-ai/headroom","slug":"invalid-rollout-worker-field-field-r","errorCode":null,"errorMessage":"invalid rollout worker field {field!r}","messagePattern":"invalid rollout worker field (.+?)","errorType":"exception","errorClass":"RolloutConfigurationError","httpStatus":null,"severity":"error","filePath":"headroom/rollout.py","lineNumber":303,"sourceCode":"    def from_internal_dict(cls, value: Mapping[str, object]) -> RolloutSnapshot:\n        \"\"\"Validate and restore a snapshot serialized for worker handoff.\"\"\"\n\n        if not isinstance(value, Mapping):\n            raise RolloutConfigurationError(\"invalid rollout worker snapshot\")\n        try:\n            if value.get(\"schema_version\") != ROLLOUT_SCHEMA_VERSION:\n                raise RolloutConfigurationError(\"unsupported rollout worker schema version\")\n            if value.get(\"policy_version\") != ROLLOUT_POLICY_VERSION:\n                raise RolloutConfigurationError(\"rollout worker policy version mismatch\")\n            channel = RolloutChannel.parse(str(value[\"channel\"]), strict=True)\n            unsafe = value[\"unsafe_allow_unstable\"]\n            if not isinstance(unsafe, bool):\n                raise RolloutConfigurationError(\"invalid rollout worker unsafe override\")\n\n            def names(field: str) -> set[str]:\n                raw = value[field]\n                if not isinstance(raw, list) or not all(isinstance(item, str) for item in raw):\n                    raise RolloutConfigurationError(f\"invalid rollout worker field {field!r}\")\n                return set(_validate_names(set(raw), source=field, strict=True))\n\n            snapshot = _resolve_snapshot(\n                channel=channel,\n                explicit_requested=names(\"explicit_requested\"),\n                explicit_disabled=names(\"explicit_disabled\"),\n                legacy_requested=names(\"legacy_requested\"),\n                legacy_disabled=names(\"legacy_disabled\"),\n                unsafe=unsafe,\n            )\n        except (KeyError, TypeError) as exc:\n            raise RolloutConfigurationError(\"invalid rollout worker snapshot\") from exc\n        if value.get(\"registry_digest\") != snapshot.registry_digest:\n            raise RolloutConfigurationError(\"rollout worker registry digest mismatch\")\n        if value.get(\"snapshot_digest\") != snapshot.snapshot_digest:\n            raise RolloutConfigurationError(\"rollout worker snapshot digest mismatch\")\n        return snapshot\n","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/rollout.py#L285-L321","documentation":"Raised inside from_internal_dict()'s names() helper when one of the four feature-name list fields ('explicit_requested', 'explicit_disabled', 'legacy_requested', 'legacy_disabled') is not a list of strings. The message names the offending field so you can pinpoint which key is malformed.","triggerScenarios":"from_internal_dict() with 'explicit_requested': 'memory' (a bare string), a list containing non-strings ([1, 'memory']), or a dict/None where a list is expected.","commonSituations":"Compressing single-element lists to a scalar in config; JSON round-trips that change types; YAML anchors producing dicts where lists were intended.","solutions":["Make every feature-name field a JSON array of strings, e.g. ['memory', 'context'].","Wrap scalar shortcuts in a list before restore: value if isinstance(value, list) else [value].","Validate the handoff payload shape with a JSON schema at the producing side."],"exampleFix":"# before\n{\"explicit_requested\": \"memory\", ...}\n\n# after\n{\"explicit_requested\": [\"memory\"], ...}","handlingStrategy":"type-guard","validationCode":"NAME_FIELDS = ('explicit_requested', 'explicit_disabled', 'legacy_requested', 'legacy_disabled')\nfor field in NAME_FIELDS:\n    value = payload.get(field)\n    if not isinstance(value, list) or not all(isinstance(i, str) for i in value):\n        raise ValueError(f'{field} must be a list of strings, got {value!r}')","typeGuard":"def is_string_list(value: object) -> bool:\n    return isinstance(value, list) and all(isinstance(i, str) for i in value)","tryCatchPattern":"try:\n    snapshot = RolloutSnapshot.from_internal_dict(payload)\nexcept RolloutConfigurationError as e:\n    if 'invalid rollout worker field' in str(e):\n        field = str(e).rsplit(' ', 1)[-1].strip(\"!r'\")\n        payload[field] = [payload[field]] if isinstance(payload.get(field), str) else payload.get(field, [])\n        # prefer fixing the producer instead of repairing payloads\n    raise","preventionTips":["Always emit lists of strings even for a single feature name.","Run handoff payloads through a JSON schema validator before enqueueing.","Watch for YAML/JSON config coercions that collapse one-element lists to scalars."],"tags":["serialization","rollout","type-validation","lists"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}