affaan-m/ECC · error · ValueError
cannot read overlay image
Error message
cannot read overlay image: {image} What it means
overlay() reads the overlay image with OpenCV (cv2.imread with IMREAD_UNCHANGED) before building the ffmpeg filtergraph; imread returns None for unreadable files, which this code converts into ValueError('cannot read overlay image: ...'). The read also supplies the source image dimensions used to scale the overlay element.
Solutions
- Check the path in the message exists and is a readable image file (ls -l / file <path>).
- Convert the image to PNG or JPEG (ffmpeg -i in.webp out.png) if the format is unsupported by the installed OpenCV.
- Fix the path to be absolute or ensure the script's working directory matches the asset location.
- Regenerate the overlay asset if the producing step failed and left no file.
- Pre-validate in the caller: assert Path(image).is_file() before calling overlay().
Example fix
// before
assemble.overlay(video, "lgoo.png", ...) # typo -> ValueError
// after
from pathlib import Path
img = Path("logo.png")
if not img.is_file():
raise SystemExit(f"overlay asset missing: {img}")
assemble.overlay(video, str(img), ...) Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
import cv2
img = cv2.imread(str(overlay_path), cv2.IMREAD_UNCHANGED)
if img is None:
raise SystemExit(f"overlay not readable: {overlay_path}") Type guard
def is_readable_image(path) -> bool:
import cv2
from pathlib import Path
p = Path(path)
return p.is_file() and cv2.imread(str(p), cv2.IMREAD_UNCHANGED) is not None Try / catch
try:
assemble.overlay(video, logo_path, ...)
except ValueError as exc:
if str(exc).startswith("cannot read overlay image"):
log.error("bad overlay asset: %s", exc)
raise Prevention
- Resolve overlay paths to absolute paths before calling overlay()
- Convert assets to PNG/JPEG for maximum OpenCV compatibility
- Verify asset-producing steps succeeded (file exists, size > 0) before use
- cv2.imread returns None, not an exception — always check for None
- Validate image readability in a preflight check across all assets
When it happens
Trigger: overlay(...) called with an image path that does not exist, is a directory, has an unsupported/corrupt format (e.g. WEBP with alpha on an old OpenCV), or has unreadable permissions — cv2.imread returns None and the raise fires.
Common situations: Typo in the logo/watermark path; asset generated by a previous step failed silently so file is missing; image is in a format the installed OpenCV build can't decode (missing codec support); relative path resolved from the wrong working directory.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/bd66a2643eaecd05.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-distillation/scripts/taste/assemble.py:113
the same mark in the same place every time.
``screen`` is the default because plates are premultiplied against black,
so screen drops their blacks for free and no matte is needed.
"""
clip, image, dst = Path(clip), Path(image), Path(dst)
if width is None or height is None:
from . import frames as _fm
info = _fm.probe(clip)
width, height = info.width, info.height
# Resolve the element's pixel size here rather than in ffmpeg expressions.
# pad() rejects a negative offset and cannot pad to a size smaller than its
# input, so an element that lands oversized or off-frame kills the whole
# filtergraph - which it did on the first attempt.
import cv2 as _cv2
_im = _cv2.imread(str(image), _cv2.IMREAD_UNCHANGED)
if _im is None:
raise ValueError(f"cannot read overlay image: {image}")
ih0, iw0 = _im.shape[:2]
ew = max(2, int(width * max(0.02, min(1.0, scale))))
eh = max(2, int(ew * ih0 / max(1, iw0)))
if eh > height: # fit tall elements to the frame instead of overflowing
eh = height
ew = max(2, int(eh * iw0 / max(1, ih0)))
ew, eh = min(ew, width), min(eh, height)
if isinstance(position, tuple):
px = int(width * position[0])
py = int(height * position[1])
else:
anchors = {
"center": (0.5, 0.5), "top": (0.5, 0.12), "bottom": (0.5, 0.88),
"left": (0.14, 0.5), "right": (0.86, 0.5),
"topleft": (0.16, 0.16), "topright": (0.84, 0.16),
"bottomleft": (0.16, 0.84), "bottomright": (0.84, 0.84),
}
ax, ay = anchors.get(position, (0.5, 0.5))View on GitHub (pinned to 8321021c54)