calesthio/OpenMontage · error · ToolCommandError

{detail}

Error message

{detail}

What it means

Raised by BaseTool.run_command when a subprocess it launched exits non-zero: subprocess.run(..., check=True) raises CalledProcessError, which is wrapped into ToolCommandError carrying returncode, cmd, output, and stderr. The detail is stderr if present, else stdout, else str(exc) — so the message you see is the underlying command's own error text, not a library string. ToolCommandError subclasses subprocess.CalledProcessError, so standard attributes (returncode, cmd, stdout, stderr) are available for programmatic handling.

Source

Thrown at tools/base_tool.py:447

            return subprocess.run(
                resolved_cmd,
                capture_output=True,
                text=True,
                # Force UTF-8 decoding. The default uses the OS locale (cp1252 on
                # Windows), which raises UnicodeDecodeError on a subprocess that
                # emits Unicode/emoji (e.g. Remotion's progress output), killing the
                # reader thread and potentially swallowing the real error text.
                encoding="utf-8",
                errors="replace",
                timeout=timeout,
                cwd=cwd,
                check=True,
            )
        except subprocess.CalledProcessError as exc:
            stderr = (exc.stderr or "").strip()
            stdout = (exc.stdout or "").strip()
            detail = stderr or stdout or str(exc)
            raise ToolCommandError(
                exc.returncode,
                exc.cmd,
                output=exc.output,
                stderr=exc.stderr,
                detail=detail,
            ) from exc


class ToolCommandError(subprocess.CalledProcessError):
    """CalledProcessError with stderr/stdout surfaced in str(error)."""

    def __init__(
        self,
        returncode: int,
        cmd: list[str],
        *,
        output: Optional[str] = None,
        stderr: Optional[str] = None,

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Read e.stderr/e.stdout from the caught ToolCommandError — they contain the actual command diagnostics
  2. Reproduce manually: run e.cmd in a shell with the same arguments to iterate quickly
  3. Fix the concrete underlying issue (input file, codec, flags, permissions, disk space)
  4. Pin or adapt to the external tool's version if flags changed; never retry blindly on deterministic argument errors

Example fix

# before
try:
    tool.run_command(["ffmpeg", "-i", src, out])
except ToolCommandError as e:
    raise RuntimeError(f"command failed: {e}")  # loses diagnostics

# after
try:
    tool.run_command(["ffmpeg", "-i", src, out])
except ToolCommandError as e:
    log.error("cmd=%s rc=%s stderr=%s", e.cmd, e.returncode, (e.stderr or "").strip())
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

import shutil
if not shutil.which(cmd[0]):
    raise SystemExit(f"command {cmd[0]} not installed")
for f in input_files:
    if not Path(f).is_file():
        raise SystemExit(f"input missing: {f}")

Try / catch

from tools.base_tool import ToolCommandError
try:
    tool.run_command(cmd)
except ToolCommandError as e:
    log.error("cmd=%s rc=%s\nstderr=%s", e.cmd, e.returncode, (e.stderr or "").strip())
    if e.returncode in (13,):  # deterministic permission error: do not retry
        raise
    if is_transient(e):  # e.g. network-flaky downloader
        return tool.run_command(cmd)  # single retry
    raise

Prevention

When it happens

Trigger: Any tool shelling out to ffmpeg/ffprobe/node/CLI binaries with invalid arguments, unreadable input files, unsupported codecs, or write-permission failures on the output path. E.g. ffmpeg exiting 1 on a corrupt input surfaces its stderr here.

Common situations: Bad file paths or codec mismatches passed to ffmpeg; a CLI tool's version changing its flags; disk-full or permission-denied on output; non-UTF8 output already mitigated by errors='replace', so the real error text survives; command timeouts surface separately.

Related errors


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