pbakaus/impeccable · warning
[impeccable] visual contrast scan failed
Error message
[impeccable] visual contrast scan failed
What it means
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.
Source
Thrown at browser-bundle/50-scan.js:214
function postExtensionError(err) {
if (!EXTENSION_MODE) return;
window.postMessage({
source: 'impeccable-error',
message: err?.message || String(err),
}, '*');
}
function reportVisualContrastError(err, detail = {}) {
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-error', {
detail: {
...detail,
message: err?.message || String(err),
},
}));
if (EXTENSION_MODE) {
postExtensionError(err);
} else {
console.warn('[impeccable] visual contrast scan failed', err);
}
}
function scheduleLazyVisualContrast(groupMap, analyses, options = {}, runtime = {}) {
disconnectLazyVisualContrastObserver();
if (options.visualContrastLazy === false || options.scrollOffscreen !== false) return;
if (typeof IntersectionObserver === 'undefined') return;
const unresolved = __lazyVisualContrastCandidates(analyses);
if (unresolved.length === 0) return;
const generation = runtime.generation || scanGeneration;
lazyVisualContrastObserver = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
const el = entry.target;
const candidate = lazyVisualContrastPending.get(el);
if (!candidate || lazyVisualContrastResolving.has(el)) continue;
lazyVisualContrastObserver?.unobserve(el);View on GitHub (pinned to 2bc2879276)
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
Example fix
// before color: 'color-mix(in srgb, red 50%, blue)' // analyzer throws, scan reports failure // after // parse via getComputedStyle resolved value, or guard: const parsed = parseColor(cs.color) ?? parseColor(cs.backgroundColor); if (!parsed) return null; // skip instead of throwing
Defensive patterns
Strategy: try-catch
Try / catch
try {
await scanContrast(groupMap, analyses, options, runtime);
} catch (err) {
reportVisualContrastError(err); // logs '[impeccable] visual contrast scan failed'
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- [impeccable] visual contrast scan failed
- [impeccable] scan failed
- embed-prompt: --scan needs at least one directory
- Error: cannot scan {target}: {message}
- [impeccable] scan failed
AI-assisted analysis of pbakaus/impeccable@2bc2879276 (2026-09-08).
Data as JSON: /api/errors/7a65fe48d0a4e791.
Report an issue: GitHub.