headroomlabs-ai/headroom · error · AttributeError
module {__name__!r} has no attribute {name!r}
Error message
module {__name__!r} has no attribute {name!r} What it means
headroom.transforms implements lazy exports via a module-level __getattr__: names listed in _LAZY_EXPORTS are imported on first access so heavy modules (e.g. the HTML extractor) load only when used. Any attribute not in globals(), __all__, or _LAZY_EXPORTS raises AttributeError with the module's __name__ and the missing name — the standard 'no attribute' semantics, just produced by the lazy loader.
Source
Thrown at headroom/transforms/__init__.py:221
"CacheAligner": ("headroom.transforms.cache_aligner", "CacheAligner"),
# HTML extraction (optional dependency - requires trafilatura)
"HTMLExtractor": ("headroom.transforms.html_extractor", "HTMLExtractor"),
"HTMLExtractorConfig": ("headroom.transforms.html_extractor", "HTMLExtractorConfig"),
"HTMLExtractionResult": ("headroom.transforms.html_extractor", "HTMLExtractionResult"),
"is_html_content": ("headroom.transforms.html_extractor", "is_html_content"),
}
def __getattr__(name: str) -> object:
if name == "__path__":
raise AttributeError(name)
if name == "_HTML_EXTRACTOR_AVAILABLE":
return _HTML_EXTRACTOR_AVAILABLE
try:
module_name, attr_name = _LAZY_EXPORTS[name]
except KeyError as exc:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from exc
module = import_module(module_name)
value = getattr(module, attr_name)
globals()[name] = value
return value
def __dir__() -> list[str]:
return sorted(set(globals()) | set(__all__))
View on GitHub (pinned to 322425c43b)
Solutions
- Check the package __all__/docs for the exact public spelling and use it.
- If the symbol genuinely exists in a submodule but is not re-exported, import from the full path: from headroom.transforms.html import extract_x.
- On version upgrades, grep the changelog for renames of the transform you use.
- Use dir(headroom.transforms) — __dir__ merges globals and __all__ — to see what is addressable.
Example fix
# before from headroom.transforms import strip_html # AttributeError: not exported # after import headroom.transforms as T print([n for n in dir(T) if "html" in n.lower()]) from headroom.transforms import HtmlStripTransform # exact exported name
Defensive patterns
Strategy: type-guard
Validate before calling
import headroom.transforms as T
assert "TargetSymbol" in dir(T), f"not exported; available: {[n for n in dir(T)]}" Type guard
def exports(name: str) -> bool:
import headroom.transforms as T
return name in dir(T) Try / catch
try:
from headroom.transforms import TargetSymbol
except AttributeError as e:
raise ImportError(f"{e}; check headroom.transforms.__all__ for the public name") from e Prevention
- Import from explicit submodule paths for symbols not in __all__.
- Use dir(module) (it merges __all__) to discover lazy exports.
- Pin versions and update imports on rename announcements.
When it happens
Trigger: from headroom.transforms import StripHTMLTypo; transforms.SomeTransform (name not exported under that spelling); accessing a symbol that lives in a submodule directly (headroom.transforms.html.extract) but was never re-exported in _LAZY_EXPORTS/__all__.
Common situations: IDE autocompleting a private helper; names renamed between versions (old imports break); users assuming every symbol in submodules is re-exported at the package root; `import headroom.transforms as t; t.partial` style probing.
Related errors
- module {__name__!r} has no attribute {name!r}
- unknown Headroom harness config field: {name}
- Unknown backend: {backend}
- Unknown provider: {self.llm_config.provider}
- Unknown compression-only benchmark: {spec.name}
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/c17b46b65949d9d7.
Report an issue: GitHub.