{"record":{"id":"7a65fe48d0a4e791","repo":"pbakaus/impeccable","slug":"impeccable-visual-contrast-scan-failed-7a65fe","errorCode":null,"errorMessage":"[impeccable] visual contrast scan failed","messagePattern":"\\[impeccable\\] visual contrast scan failed","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"browser-bundle/50-scan.js","lineNumber":214,"sourceCode":"  function postExtensionError(err) {\n    if (!EXTENSION_MODE) return;\n    window.postMessage({\n      source: 'impeccable-error',\n      message: err?.message || String(err),\n    }, '*');\n  }\n\n  function reportVisualContrastError(err, detail = {}) {\n    window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-error', {\n      detail: {\n        ...detail,\n        message: err?.message || String(err),\n      },\n    }));\n    if (EXTENSION_MODE) {\n      postExtensionError(err);\n    } else {\n      console.warn('[impeccable] visual contrast scan failed', err);\n    }\n  }\n\n  function scheduleLazyVisualContrast(groupMap, analyses, options = {}, runtime = {}) {\n    disconnectLazyVisualContrastObserver();\n    if (options.visualContrastLazy === false || options.scrollOffscreen !== false) return;\n    if (typeof IntersectionObserver === 'undefined') return;\n    const unresolved = __lazyVisualContrastCandidates(analyses);\n    if (unresolved.length === 0) return;\n    const generation = runtime.generation || scanGeneration;\n\n    lazyVisualContrastObserver = new IntersectionObserver((entries) => {\n      for (const entry of entries) {\n        if (!entry.isIntersecting) continue;\n        const el = entry.target;\n        const candidate = lazyVisualContrastPending.get(el);\n        if (!candidate || lazyVisualContrastResolving.has(el)) continue;\n        lazyVisualContrastObserver?.unobserve(el);","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/pbakaus/impeccable/blob/2bc2879276c1f321a53c4ca99d3371e411329b52/browser-bundle/50-scan.js#L196-L232","documentation":"reportVisualContrastError is the top-level handler for the visual contrast scanner in the browser extension bundle. When a contrast analysis throws (e.g. while computing contrast ratios during a lazy IntersectionObserver-driven scan), it logs '[impeccable] visual contrast scan failed' with the error via console.warn (or posts it to the extension error channel in EXTENSION_MODE) instead of crashing the scan pipeline.","triggerScenarios":"Any exception inside scan/scheduleLazyVisualContrast during contrast analysis — e.g. getComputedStyle on a detached element, malformed color values (like 'color-mix(...)' the parser doesn't understand), or a DOM mutation removing observed nodes mid-analysis.","commonSituations":"Pages using modern CSS color functions not yet parsed by the analyzer; elements unmounted between observation and scan; shadow-DOM content the scanner can't reach.","solutions":["Check the logged error object's message in the console — the scanner keeps working; the failure only skips that element/group","Identify the element/color that broke parsing (modern CSS color functions are the usual culprit) and reproduce on a minimal page","Update/patch the color parser to handle the failing CSS color syntax","If in extension mode, verify postExtensionError received the error so it is reported rather than only warned"],"exampleFix":"// before\ncolor: 'color-mix(in srgb, red 50%, blue)' // analyzer throws, scan reports failure\n// after\n// parse via getComputedStyle resolved value, or guard:\nconst parsed = parseColor(cs.color) ?? parseColor(cs.backgroundColor);\nif (!parsed) return null; // skip instead of throwing","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await scanContrast(groupMap, analyses, options, runtime);\n} catch (err) {\n  reportVisualContrastError(err); // logs '[impeccable] visual contrast scan failed'\n}","preventionTips":["Always read the logged error — scan failures are per-element and non-fatal","Guard against detached elements: re-check el.isConnected inside analysis callbacks","Support/parse modern CSS color functions or skip unparseable colors instead of throwing","Observe DOM mutations and cancel in-flight scans when nodes are removed mid-analysis"],"tags":["extension","accessibility","contrast","scan"],"backgroundTag":"unhandled-scan-exception","analyzedSha":"2bc2879276c1f321a53c4ca99d3371e411329b52","analyzedAt":"2026-09-08T04:51:14.109Z","contentChangedAt":"2026-09-08T04:51:14.109Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}