{"record":{"id":"a1b9d1f903abf402","repo":"affaan-m/ECC","slug":"template-unreadable-template-path","errorCode":null,"errorMessage":"Template unreadable: {template_path}","messagePattern":"Template unreadable: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"skills/windows-desktop-e2e/SKILL.md","lineNumber":808,"sourceCode":"3. **Record the scale alongside each artefact.** On capture, write `GetDpiForWindow(hwnd) / 96` to `artifacts/<test>/metadata.json` — postmortems become obvious instead of guess-work.\n\n> Process-level DPI awareness (`SetProcessDpiAwarenessContext`) **can conflict with Qt's own DPI handling** when the app under test is Qt-based. Prefer \"same-scale templates + CI pin\" over flipping process-wide DPI mode in fixtures.\n\n### Debugging Match Confidence\n\nWhen tuning the `confidence` threshold, the only sane workflow is to **see** where the match landed. The helper below is diagnosis-only — do not call it from test code.\n\n```python\ndef debug_match(template_path, out=\"artifacts/match_debug.png\", confidence=0.85):\n    \"\"\"Diagnosis-only. Draw the best-match rectangle + score back on the current screen.\n\n    NOT for production tests — use when calibrating confidence or chasing false matches.\n    \"\"\"\n    import os, cv2, pyautogui, numpy as np\n    screen = np.array(pyautogui.screenshot())[:, :, ::-1]\n    tpl    = cv2.imread(template_path)\n    if tpl is None:\n        raise RuntimeError(f\"Template unreadable: {template_path}\")\n    res    = cv2.matchTemplate(screen, tpl, cv2.TM_CCOEFF_NORMED)\n    _, mv, _, ml = cv2.minMaxLoc(res)\n    h, w   = tpl.shape[:2]\n    colour = (0, 255, 0) if mv >= confidence else (0, 0, 255)  # green pass / red fail\n    cv2.rectangle(screen, ml, (ml[0]+w, ml[1]+h), colour, 2)\n    cv2.putText(screen, f\"score={mv:.3f} thr={confidence}\",\n                (ml[0], max(20, ml[1]-6)),\n                cv2.FONT_HERSHEY_SIMPLEX, 0.7, colour, 2)\n    os.makedirs(os.path.dirname(out) or \".\", exist_ok=True)\n    cv2.imwrite(out, screen)\n    return mv\n```\n\n**Use sparingly** — image matching breaks on DPI changes, theme switches, and partial occlusion.\nAlways try UIA first; fall back to screenshots only for genuinely unreachable controls.\n\n## Anti-Patterns\n","sourceCodeStart":790,"sourceCodeEnd":826,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/skills/windows-desktop-e2e/SKILL.md#L790-L826","documentation":"A RuntimeError raised by debug_match() when cv2.imread(template_path) returns None, meaning OpenCV could not read the image file. This is a diagnosis-only helper used while calibrating the confidence threshold. cv2.imread returns None (rather than raising) on failure, so the guard is mandatory.","triggerScenarios":"Calling debug_match(template_path) with a path that does not exist, is not an image, uses an unsupported format, or is unreadable due to permissions. cv2.imread silently returns None for all of these.","commonSituations":"Relative path resolved against the wrong working directory; template was never committed or was gitignored; file is .png.png due to a double extension; the image is a CMYK JPEG or 16-bit PNG that OpenCV cannot decode; permissions denied in CI.","solutions":["Check os.path.exists(template_path) and that the working directory is what you expect.","Print the absolute path and confirm the file is a supported format (PNG/JPEG/BGR-readable).","Verify the file is committed to the repo and not gitignored.","Open the file in an image editor to confirm it is not corrupt; re-export as 8-bit sRGB PNG if needed."],"exampleFix":"# before\ntpl = cv2.imread(template_path)\nif tpl is None:\n    raise RuntimeError(f\"Template unreadable: {template_path}\")\n\n# after: distinguish missing-file from unreadable\nimport os\nif not os.path.exists(template_path):\n    raise FileNotFoundError(f\"Template missing: {os.path.abspath(template_path)}\")\ntpl = cv2.imread(template_path)\nif tpl is None:\n    raise RuntimeError(f\"Template unreadable or unsupported format: {template_path}\")","handlingStrategy":"validation","validationCode":"import os\ndef template_readable(path) -> bool:\n    if not os.path.exists(path):\n        return False\n    import cv2\n    return cv2.imread(path) is not None\n\nif not template_readable(template_path):\n    raise FileNotFoundError(f\"template missing or unreadable: {os.path.abspath(template_path)}\")","typeGuard":"null","tryCatchPattern":"import os, cv2\nif not os.path.exists(template_path):\n    raise FileNotFoundError(template_path)\ntpl = cv2.imread(template_path)\nif tpl is None:\n    raise RuntimeError(f\"Template unreadable: {template_path}\")","preventionTips":["Resolve template paths to absolute paths relative to the test file.","Commit templates to the repo and verify they are not gitignored.","Re-export templates as 8-bit sRGB PNG if OpenCV cannot decode them."],"tags":["python","opencv","filesystem","ui-automation"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}