{"record":{"id":"8b4f5db89b4dc552","repo":"affaan-m/ECC","slug":"cannot-read-plate-path-plates","errorCode":null,"errorMessage":"cannot read plate: {path}","messagePattern":"cannot read plate: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-distillation/scripts/taste/plates.py","lineNumber":225,"sourceCode":"def tighten(path: str | Path, dest: str | Path | None = None, pad: float = 0.06) -> Path:\n    \"\"\"Crop a plate to its own content, so the element fills the file.\n\n    A glow plate is mostly empty by construction - the selection keeps the top\n    few percent of pixels by energy, so a typical plate is 2-7% covered and\n    97% transparent black. Compositing that at full frame produces a small\n    bright dot floating in the middle of the shot, which reads as a sticker\n    rather than as light. Measured on the first cut: a plate covering 1.7% of\n    its own frame, screen-blended full-frame, was visible only as a coloured\n    blob near centre.\n\n    Cropping to the alpha bounding box means the caller controls the element's\n    size on screen by scaling, instead of inheriting whatever fraction of the\n    source frame the element happened to occupy.\n    \"\"\"\n    path = Path(path)\n    im = cv2.imread(str(path), cv2.IMREAD_UNCHANGED)\n    if im is None:\n        raise ValueError(f\"cannot read plate: {path}\")\n    alpha = im[..., 3] if im.shape[2] == 4 else im[..., :3].max(axis=2)\n    ys, xs = np.where(alpha > 12)\n    if len(ys) == 0:\n        return path\n    h, w = alpha.shape\n    py, px = int(h * pad), int(w * pad)\n    y0 = max(0, int(ys.min()) - py); y1 = min(h, int(ys.max()) + py + 1)\n    x0 = max(0, int(xs.min()) - px); x1 = min(w, int(xs.max()) + px + 1)\n    out = Path(dest) if dest else path.with_name(path.stem + \"_tight.png\")\n    out.parent.mkdir(parents=True, exist_ok=True)\n    cv2.imwrite(str(out), im[y0:y1, x0:x1])\n    return out\n\n\ndef plate_coverage(path: str | Path) -> float:\n    \"\"\"Share of the plate that is actually lit. Drives element-vs-wash choice.\"\"\"\n    im = cv2.imread(str(path), cv2.IMREAD_UNCHANGED)\n    if im is None:","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-distillation/scripts/taste/plates.py#L207-L243","documentation":"tighten crops a plate PNG around its non-transparent pixels using OpenCV. cv2.imread returns None (rather than raising) when it cannot read a file, so the function converts that into ValueError('cannot read plate: <path>'). Common causes are a nonexistent path or an image OpenCV's codec support cannot decode (e.g. 16-bit PNG with unusual settings, or a non-image file with an image extension).","triggerScenarios":"tighten(path) where cv2.imread returns None: file does not exist at that path, the file is corrupt/truncated, the extension does not match the actual content (e.g. a text error page saved as .png), or an unsupported format/bit-depth for the installed OpenCV build.","commonSituations":"Upstream rendering/export step silently failed and left a zero-byte file; paths passed through a config with typos or stale absolute paths from another machine; non-ASCII or spaces in the path mishandled on some platforms; plate exported as WebP/16-bit that the OpenCV build can't decode.","solutions":["Check the file yourself: os.path.exists / Path.stat, file <path> to confirm it is really a PNG, and try opening it with PIL as a second decoder.","Re-export or re-render the plate if the file is corrupt or zero-byte; tighten cannot repair unreadable input.","Normalize the path: use Path and absolute paths, quote/sanitize spaces and non-ASCII characters.","If format support is the issue, convert to a plain 8-bit PNG: ffmpeg -i plate.webp plate.png (or PIL Image.open(...).convert('RGBA').save('plate.png')).","Pre-validate before calling and fall back to the original path or a clear skip when the plate can't be read."],"exampleFix":"// before\ntighten('renders/plate.png')  # ValueError: cannot read plate\n// after\nfrom pathlib import Path\np = Path('renders/plate.png')\nif not p.exists() or p.stat().st_size == 0:\n    raise SystemExit(f'plate missing or empty: {p.resolve()}')\ntighten(p)","handlingStrategy":"validation","validationCode":"from pathlib import Path\nimport cv2\np = Path(path)\nif not p.is_file() or p.stat().st_size == 0:\n    raise SystemExit(f'plate missing or empty: {p.resolve()}')\nprobe = cv2.imread(str(p), cv2.IMREAD_UNCHANGED)\nif probe is None:\n    raise SystemExit(f'not decodable by OpenCV: {p} (convert to 8-bit PNG first)')","typeGuard":null,"tryCatchPattern":"try:\n    tightened = tighten(plate)\nexcept ValueError as e:\n    logger.warning('skipping unreadable plate: %s', e)\n    tightened = plate  # fall back to the original, untightened plate","preventionTips":["After any export/render step, assert plate files are non-empty before downstream use.","Keep plates as plain 8-bit RGBA PNGs; avoid 16-bit or exotic formats for pipeline assets.","Store plate paths in a config generated by the producing step, never retyped by hand.","Sanitize paths (absolute, no stray whitespace) when moving work between machines."],"tags":["opencv","file-io","image","validation"],"backgroundTag":"file-read-failed","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}