calesthio/OpenMontage · error · RuntimeError

Playwright is required to render preview MP4

Error message

Playwright is required to render preview MP4

What it means

Raised by _render_preview_mp4 when `from playwright.sync_api import sync_playwright` raises ImportError. The preview renderer drives headless Chromium via Playwright to screenshot the HTML preview frame-by-frame, so the playwright package is a hard requirement once ffmpeg is present. The original ImportError is chained (`from exc`) so the underlying cause is preserved.

Source

Thrown at tools/character/character_animation.py:76

    visual_style = style.get("visual_style") or style.get("name") or style.get("style")
    if visual_style:
        normalized["visual_style"] = str(visual_style)
    palette = style.get("palette")
    if isinstance(palette, list):
        normalized["palette"] = [str(color) for color in palette]
    for key in ["line_style", "texture"]:
        if style.get(key):
            normalized[key] = str(style[key])
    return normalized


def _render_preview_mp4(preview_path: Path, video_path: Path, duration_seconds: float, fps: int) -> None:
    if shutil.which("ffmpeg") is None:
        raise RuntimeError("ffmpeg is required to render preview MP4")
    try:
        from playwright.sync_api import sync_playwright
    except Exception as exc:  # pragma: no cover - dependency-specific branch
        raise RuntimeError("Playwright is required to render preview MP4") from exc

    frame_dir = video_path.parent / f"{video_path.stem}_frames"
    frame_dir.mkdir(parents=True, exist_ok=True)
    frame_count = max(2, int(duration_seconds * fps))
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page(viewport={"width": 1280, "height": 720})
        page.goto(preview_path.resolve().as_uri(), wait_until="networkidle")
        for frame in range(frame_count):
            if frame:
                page.wait_for_timeout(int(1000 / fps))
            page.screenshot(path=str(frame_dir / f"frame_{frame:04d}.png"))
        browser.close()

    cmd = [
        "ffmpeg",
        "-y",
        "-framerate",

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Install playwright into the same interpreter that runs the tool: `pip install playwright`.
  2. Install the browser binary: `playwright install chromium` (otherwise the subsequent p.chromium.launch() will fail with its own error).
  3. If it is installed but still ImportError, verify with the running interpreter: `python -c "from playwright.sync_api import sync_playwright; print('ok')"` to detect venv mismatch.
  4. Add playwright + the chromium browser step to CI/Docker so preview rendering works out of the box.

Example fix

# before
pip install -r requirements.txt  # playwright missing -> RuntimeError

# after
pip install playwright
playwright install chromium
Defensive patterns

Strategy: validation

Validate before calling

def playwright_available() -> bool:
    try:
        from playwright.sync_api import sync_playwright  # noqa: F401
        return True
    except ImportError:
        return False

assert playwright_available(), "pip install playwright && playwright install chromium"

Try / catch

try:
    from playwright.sync_api import sync_playwright
except ImportError as e:
    raise RuntimeError("Install playwright: pip install playwright && playwright install chromium") from e

Prevention

When it happens

Trigger: Reaching _render_preview_mp4 with ffmpeg installed but the playwright pip package missing (not in requirements, installed in a different venv, or the process runs under a different interpreter). Note the package install alone is not enough — `playwright install chromium` is also needed later.

Common situations: Installing only the project's core deps without the preview extras, mixing system Python and virtualenv, or CI that installs playwright but forgets the browser download step and fails one step later with a different error.

Related errors


AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15). Data as JSON: /api/errors/2592913976bc32b7. Report an issue: GitHub.