headroomlabs-ai/headroom · error · ImportError

jinja2 is required for report generation. Install with: pip

Error message

jinja2 is required for report generation. Install with: pip install headroom[reports]

What it means

Raised by _get_jinja2_template() in the report generator when the jinja2 import fails while rendering a report. HTML report generation is an optional feature backed by the [reports] extra; the import is lazy so the error appears at render time, not at import of the module.

Source

Thrown at headroom/reporting/generator.py:23

from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING, Any

from ..storage import create_storage
from ..utils import estimate_cost, format_cost

if TYPE_CHECKING:
    pass


def _get_jinja2_template(template_str: str):
    """Lazily import jinja2 and create template."""
    try:
        from jinja2 import Template

        return Template(template_str)
    except ImportError as e:
        raise ImportError(
            "jinja2 is required for report generation. Install with: pip install headroom[reports]"
        ) from e


# HTML template embedded as string
REPORT_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Headroom Report - {{ generated_at }}</title>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

View on GitHub (pinned to 322425c43b)

Solutions

  1. Install the extra: pip install 'headroom[reports]'.
  2. Check for jinja2 in the deployment image: python -c "import jinja2".
  3. Disable report generation in configs for deployments that do not need it.

Example fix

# before
html = generator.render()  # ImportError: jinja2 required

# after
# pip install 'headroom[reports]'
html = generator.render()
Defensive patterns

Strategy: fallback

Validate before calling

def jinja2_available() -> bool:
    try:
        import jinja2  # noqa: F401
        return True
    except ImportError:
        return False

if not jinja2_available():
    skip_report_generation()  # or install check in deploy pipeline

Type guard

def jinja2_available() -> bool:
    try:
        import jinja2  # noqa: F401
        return True
    except ImportError:
        return False

Try / catch

try:
    html = generator.render()
except ImportError as e:
    if 'jinja2' in str(e):
        logger.warning('reports disabled: %s', e)
    else:
        raise

Prevention

When it happens

Trigger: Calling the report generator's render/generate entry point that builds REPORT_TEMPLATE through _get_jinja2_template() without jinja2 installed.

Common situations: Base install of headroom used for proxying only, then someone enables report generation; CI image missing extras; jinja2 removed as a transitive dependency of another package.

Related errors


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