dgtlmoon/changedetection.io · error · PDFToHTMLToolNotFound

Command-line `{tool}` tool was not found in system PATH, was

Error message

Command-line `{tool}` tool was not found in system PATH, was it installed?

What it means

PDFToHTMLToolNotFound raised by preprocess_pdf in the text_json_diff processor when the external pdftohtml binary (or the name given in PDF_TO_HTML_TOOL) cannot be found via shutil.which. Watching a PDF URL requires this poppler-utils tool to be installed on the host/container.

Source

Thrown at changedetectionio/processors/text_json_diff/processor.py:297

        Supports two RSS processing modes:
        - 'default': Inline CDATA replacement (original behavior)
        - 'formatted': Format RSS items with title, link, guid, pubDate, and description (CDATA unmarked)
        """
        from changedetectionio import rss_tools
        rss_mode = self.datastore.data["settings"]["application"].get("rss_reader_mode")
        if rss_mode:
            # Format RSS items nicely with CDATA content unmarked and converted to text
            return rss_tools.format_rss_items(content)
        else:
            # Default: Original inline CDATA replacement
            return cdata_in_document_to_text(html_content=content)

    def preprocess_pdf(self, raw_content):
        """Convert PDF to HTML using external tool."""
        from shutil import which
        tool = os.getenv("PDF_TO_HTML_TOOL", "pdftohtml")
        if not which(tool):
            raise PDFToHTMLToolNotFound(
                f"Command-line `{tool}` tool was not found in system PATH, was it installed?"
            )

        import subprocess
        proc = subprocess.Popen(
            [tool, '-stdout', '-', '-s', 'out.pdf', '-i'],
            stdout=subprocess.PIPE,
            stdin=subprocess.PIPE
        )
        proc.stdin.write(raw_content)
        proc.stdin.close()
        html_content = proc.stdout.read().decode('utf-8')
        proc.wait(timeout=60)

        # Add metadata for change detection
        metadata = (
            f"<p>Added by changedetection.io: Document checksum - "
            f"{hashlib.md5(raw_content).hexdigest().upper()} "

View on GitHub (pinned to 5d9c7c6da7)

Solutions

  1. Install poppler-utils: apt-get install -y poppler-utils (Debian/Ubuntu) or apk add poppler-utils (Alpine)
  2. If using a custom converter, set PDF_TO_HTML_TOOL to the full path of an existing executable
  3. Verify with: which pdftohtml && pdftohtml -v
  4. Rebuild your Docker image FROM the changedetectionio image that includes tools, or add the install to your Dockerfile

Example fix

# before (Dockerfile)
FROM python:3.12-slim
COPY . /app
# after
FROM python:3.12-slim
RUN apt-get update && apt-get install -y poppler-utils && rm -rf /var/lib/apt/lists/*
COPY . /app
Defensive patterns

Strategy: validation

Validate before calling

import os
from shutil import which
tool = os.getenv('PDF_TO_HTML_TOOL', 'pdftohtml')
if not which(tool):
    raise SystemExit(f'Install poppler-utils or set PDF_TO_HTML_TOOL to a valid binary')

Try / catch

try:
    handler.run_changedetection(watch, ...)
except PDFToHTMLToolNotFound:
    alert_ops('pdftohtml missing on worker host')

Prevention

When it happens

Trigger: A watch whose content-type is PDF triggers preprocess_pdf; os.getenv('PDF_TO_HTML_TOOL', 'pdftohtml') is resolved against PATH and which() returns None because poppler-utils is not installed or the container lacks the binary.

Common situations: Running the changedetection.io Docker image variant without the extra tools; custom/k8s deployments missing poppler-utils; a custom tool name in PDF_TO_HTML_TOOL that is misspelled or not on PATH; alpine/slim base images.


AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27). Data as JSON: /api/errors/69c2954340d75440. Report an issue: GitHub.