{"record":{"id":"f452f5809796f8a5","repo":"headroomlabs-ai/headroom","slug":"unknown-rollout-channel-value-r","errorCode":null,"errorMessage":"unknown rollout channel {value!r}","messagePattern":"unknown rollout channel (.+?)","errorType":"exception","errorClass":"RolloutConfigurationError","httpStatus":null,"severity":"error","filePath":"headroom/rollout.py","lineNumber":59,"sourceCode":"    def parse(cls, value: str | None, *, strict: bool = False) -> RolloutChannel:\n        if not value:\n            return cls.STABLE\n        normalized = value.strip().lower().replace(\"-\", \"_\")\n        aliases = {\n            \"prod\": cls.STABLE,\n            \"production\": cls.STABLE,\n            \"preview\": cls.BETA,\n            \"nightly\": cls.CANARY,\n            \"development\": cls.DEV,\n        }\n        if normalized in aliases:\n            return aliases[normalized]\n        try:\n            return cls(normalized)\n        except ValueError:\n            message = f\"unknown rollout channel {value!r}\"\n            if strict:\n                raise RolloutConfigurationError(message) from None\n            logger.warning(\"%s; falling back to 'stable'\", message)\n            return cls.STABLE\n\n    @property\n    def order(self) -> int:\n        return {\n            RolloutChannel.STABLE: 0,\n            RolloutChannel.BETA: 1,\n            RolloutChannel.CANARY: 2,\n            RolloutChannel.DEV: 3,\n        }[self]\n\n    def allows(self, required: RolloutChannel) -> bool:\n        return self.order >= required.order\n\n\nclass FeatureDecisionReason(str, Enum):\n    DEFAULT = \"default\"","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/rollout.py#L41-L77","documentation":"Raised by RolloutChannel.parse() in strict mode when the value is neither a valid enum member nor one of the aliases ('production', 'preview', 'nightly', 'development'), after lowercasing. In non-strict mode it only logs a warning and falls back to STABLE, so the exception surfaces only when the caller demands strict validation (e.g. worker snapshot restore).","triggerScenarios":"Calling RolloutChannel.parse('prod', strict=True), parse('staging', strict=True), or parsing a snapshot whose 'channel' field is garbage; non-strict calls never raise — they warn and return STABLE.","commonSituations":"Abbreviations like 'prod' or 'staging' not in the alias table; a renamed channel in a newer version deserializing an older snapshot; free-text channel fields from user config.","solutions":["Use a canonical channel name: 'stable', 'beta', 'canary', 'dev', or an alias 'production'/'preview'/'nightly'/'development'.","Normalize your config's channel vocabulary to these names before parse().","If a bad channel should not be fatal, call parse() without strict=True and accept the STABLE fallback (a warning is logged)."],"exampleFix":"# before\nchannel = RolloutChannel.parse(cfg['channel'], strict=True)  # 'prod'\n\n# after\nchannel = RolloutChannel.parse('production', strict=True)","handlingStrategy":"validation","validationCode":"ALIASES = {'stable', 'beta', 'canary', 'dev', 'production', 'preview', 'nightly', 'development'}\nnormalized = channel.strip().lower()\nif normalized not in ALIASES:\n    raise ConfigError(f'unknown rollout channel: {channel!r}')\nparsed = RolloutChannel.parse(channel, strict=True)","typeGuard":"def is_rollout_channel(value: str) -> bool:\n    v = (value or '').strip().lower()\n    aliases = {'stable', 'beta', 'canary', 'dev', 'production', 'preview', 'nightly', 'development'}\n    return v in aliases","tryCatchPattern":"from headroom.rollout import RolloutChannel, RolloutConfigurationError\n\ntry:\n    channel = RolloutChannel.parse(value, strict=True)\nexcept RolloutConfigurationError:\n    channel = RolloutChannel.STABLE  # explicit fallback with logging","preventionTips":["Restrict the channel field in user-facing config to the documented names.","Decide explicitly whether unknown channels are fatal (strict) or fall back to stable.","Keep the alias list in sync when upgrading headroom — new channels may appear."],"tags":["configuration","rollout","validation","enum"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}