headroomlabs-ai/headroom · error · ValueError

compressor descriptor.name must be non-empty

Error message

compressor descriptor.name must be non-empty

What it means

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.

Source

Thrown at headroom/transforms/compressor_registry.py:184

    def register(self, compressor: Compressor, *, replace: bool = False) -> str:
        """Register ``compressor`` under its ``descriptor.name``.

        Args:
            compressor: The compressor to register.
            replace: If ``True``, replace an existing registration of the same
                name instead of raising.

        Returns:
            The registered name.

        Raises:
            ValueError: If the name is empty, or already registered and
                ``replace`` is ``False``.
        """
        name = compressor.descriptor.name
        if not name:
            raise ValueError("compressor descriptor.name must be non-empty")
        if name in self._compressors and not replace:
            raise ValueError(f"compressor {name!r} is already registered")
        self._compressors[name] = compressor
        return name

    def get(self, name: str) -> Compressor | None:
        """Return the registered compressor named ``name``, or ``None``."""
        return self._compressors.get(name)

    def names(self) -> list[str]:
        """Return all registered compressor names, sorted."""
        return sorted(self._compressors)

    def descriptors(self) -> list[CompressorDescriptor]:
        """Return the descriptors of all registered compressors, sorted by name."""
        return [self._compressors[n].descriptor for n in sorted(self._compressors)]

    def discover(self, *, replace: bool = False) -> list[str]:

View on GitHub (pinned to 322425c43b)

Solutions

  1. Set a unique, non-empty descriptor.name before registering.
  2. If descriptors come from config, fail fast at parse time on a missing/empty name with the config path in the message.
  3. Add a test that every shipped compressor registers successfully.

Example fix

# before
reg.register(MyCompressor(CompressorDescriptor(name="")))

# after
reg.register(MyCompressor(CompressorDescriptor(name="smart-crusher")))
Defensive patterns

Strategy: validation

Validate before calling

name = compressor.descriptor.name
if not name:
    raise ConfigError("compressor descriptor.name missing")
reg.register(compressor)

Type guard

def has_valid_descriptor_name(c: Compressor) -> bool:
    return bool(getattr(c.descriptor, "name", ""))

Try / catch

try:
    reg.register(compressor)
except ValueError as e:
    if "must be non-empty" in str(e):
        raise ConfigError(f"compressor from {source!r} has no name") from e
    raise

Prevention

When it happens

Trigger: Registering a compressor whose CompressorDescriptor was built with name="" (or the field was omitted and defaulted empty in a dict-based construction).

Common situations: 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.

Related errors


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