affaan-m/ECC · error · ValueError

cannot read overlay image

Error message

cannot read overlay image: {image}

What it means

overlay() raises this ValueError when cv2.imread cannot load the overlay image, returning None. This guards the rest of the function (dimension computation and ffmpeg filtergraph construction) from crashing on an unreadable path. It means the path is wrong, the file is corrupt, or the format is unsupported by OpenCV.

Solutions

  1. Verify the image path exists and is a readable file (os.path.isfile) before calling overlay()
  2. Use a raster format OpenCV supports (PNG/JPEG); convert SVG/HEIC assets to PNG first
  3. Check file permissions on the image
  4. Load the image yourself with cv2.imread first and confirm it is not None to catch the problem early

Example fix

// before
overlay(frame_src, "logo.svg", ...)  # OpenCV cannot read SVG
// after
overlay(frame_src, "logo.png", ...)  # pre-converted raster image
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
import cv2

def ensure_readable_overlay(image):
    p = Path(image)
    if not p.is_file():
        raise FileNotFoundError(p)
    if cv2.imread(str(p), cv2.IMREAD_UNCHANGED) is None:
        raise ValueError(f"unreadable/unsupported image: {p}")

Type guard

def is_valid_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:
    overlay(src, image, ...)
except ValueError as e:
    if str(e).startswith("cannot read overlay image"):
        print(f"bad overlay asset: {e}")
    raise

Prevention

When it happens

Trigger: Calling overlay() with an image path that does not exist, is a directory, has an unsupported/undetectable extension, contains truncated/corrupt data, or uses characters/permissions that block reading — cv2.imread returns None and this error fires.

Common situations: Typo'd or relative path resolved from the wrong working directory; an SVG or other non-raster format OpenCV can't decode; an image downloaded incompletely; permission-restricted asset paths.

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/09cc8117a340dc86. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/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)