{"record":{"id":"f2d1203195770a7f","repo":"headroomlabs-ai/headroom","slug":"compressor-descriptor-name-must-be-non-empty","errorCode":null,"errorMessage":"compressor descriptor.name must be non-empty","messagePattern":"compressor descriptor\\.name must be non-empty","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"headroom/transforms/compressor_registry.py","lineNumber":184,"sourceCode":"\n    def register(self, compressor: Compressor, *, replace: bool = False) -> str:\n        \"\"\"Register ``compressor`` under its ``descriptor.name``.\n\n        Args:\n            compressor: The compressor to register.\n            replace: If ``True``, replace an existing registration of the same\n                name instead of raising.\n\n        Returns:\n            The registered name.\n\n        Raises:\n            ValueError: If the name is empty, or already registered and\n                ``replace`` is ``False``.\n        \"\"\"\n        name = compressor.descriptor.name\n        if not name:\n            raise ValueError(\"compressor descriptor.name must be non-empty\")\n        if name in self._compressors and not replace:\n            raise ValueError(f\"compressor {name!r} is already registered\")\n        self._compressors[name] = compressor\n        return name\n\n    def get(self, name: str) -> Compressor | None:\n        \"\"\"Return the registered compressor named ``name``, or ``None``.\"\"\"\n        return self._compressors.get(name)\n\n    def names(self) -> list[str]:\n        \"\"\"Return all registered compressor names, sorted.\"\"\"\n        return sorted(self._compressors)\n\n    def descriptors(self) -> list[CompressorDescriptor]:\n        \"\"\"Return the descriptors of all registered compressors, sorted by name.\"\"\"\n        return [self._compressors[n].descriptor for n in sorted(self._compressors)]\n\n    def discover(self, *, replace: bool = False) -> list[str]:","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/transforms/compressor_registry.py#L166-L202","documentation":"Raised by CompressorRegistry.register when compressor.descriptor.name is an empty string. The registry keys compressors by name for later get(name) lookups and router wiring, so an empty name cannot be addressed and is rejected before insertion.","triggerScenarios":"Registering a compressor whose CompressorDescriptor was built with name=\"\" (or the field was omitted and defaulted empty in a dict-based construction).","commonSituations":"Building a descriptor from config where the name key is missing; a dataclass default of \"\" leaking through; programmatic registration in a loop where the name variable is unset for one entry.","solutions":["Set a unique, non-empty descriptor.name before registering.","If descriptors come from config, fail fast at parse time on a missing/empty name with the config path in the message.","Add a test that every shipped compressor registers successfully."],"exampleFix":"# before\nreg.register(MyCompressor(CompressorDescriptor(name=\"\")))\n\n# after\nreg.register(MyCompressor(CompressorDescriptor(name=\"smart-crusher\")))","handlingStrategy":"validation","validationCode":"name = compressor.descriptor.name\nif not name:\n    raise ConfigError(\"compressor descriptor.name missing\")\nreg.register(compressor)","typeGuard":"def has_valid_descriptor_name(c: Compressor) -> bool:\n    return bool(getattr(c.descriptor, \"name\", \"\"))","tryCatchPattern":"try:\n    reg.register(compressor)\nexcept ValueError as e:\n    if \"must be non-empty\" in str(e):\n        raise ConfigError(f\"compressor from {source!r} has no name\") from e\n    raise","preventionTips":["Require descriptor.name in config schemas (minLength: 1).","Fail at startup on unnamed compressors, not lazily at first use.","Test that every built-in compressor registers."],"tags":["registry","validation","configuration"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}