pbakaus/impeccable · error · Error
the offscreen adapter is asynchronous
Error message
the offscreen adapter is asynchronous
What it means
In the extension's offscreen document, there is no live DOM to query; the visual-contrast adapter is asynchronous (it resolves element data from a loaded snapshot via WASM). Calling coreSync() — the synchronous core accessor of the adapter interface — is unsupported there, so it throws this explicit error rather than silently returning something wrong.
Source
Thrown at browser-bundle/60-offscreen.js:89
}
// The visual-contrast IO over the snapshot: the core over the loaded
// snapshot (hit-test needs answered by the content script between calls),
// node = snapshot id, images and pixels read by the content script.
function createOffscreenVisualIO(wasm, session) {
async function core(fn, ...args) {
for (;;) {
const out = wasm[fn](...args);
if (!wasm.snapshot_has_needs()) return out;
const needs = JSON.parse(wasm.snapshot_take_needs());
const facts = await ask(session, { ask: { hitTests: needs.hitTests || [] } });
wasm.snapshot_add_facts(JSON.stringify(facts || { hits: [] }));
}
}
const media = (id) => JSON.parse(wasm.snapshot_media(id)) || {};
return {
core,
coreSync() { throw new Error('the offscreen adapter is asynchronous'); },
node: (handle) => handle,
handle: (id) => id,
parentOrBody: (id) => wasm.snapshot_parent_or_body(id),
intrinsicImg(id) { const m = media(id); return [m.nw || m.vw || m.w || 0, m.nh || m.vh || m.h || 0]; },
intrinsicRaster(id) { const m = media(id); return [m.w || m.vw || 0, m.h || m.vh || 0]; },
imgSrc(id) { const m = media(id); return m.cur || m.src || ''; },
loadImage: (src) => ask(session, { ask: { io: { kind: 'loadImage', src } } }),
readPixel: (ref, plan, px, py) => ask(session, { ask: { io: { kind: 'readPixel', ref, plan, px, py } } }),
// Scrolling the page from a snapshot is not meaningful; the extension
// never sets scrollOffscreen, and the lazy pass re-captures instead.
querySelector: () => null,
scroll() { const v = JSON.parse(wasm.snapshot_viewport()) || {}; return { x: v.scrollX || 0, y: v.scrollY || 0 }; },
scrollTo() {},
scrollIntoView: () => false,
waitForPaint: () => Promise.resolve(),
};
}
View on GitHub (pinned to 2bc2879276)
Solutions
- Use the async IO.core(name, argsJson) accessor instead of coreSync in offscreen contexts
- Branch your code: use the sync adapter only on live pages, the offscreen adapter for snapshot-based scans
- Refactor the calling code to await the async core call
Example fix
// before
const out = JSON.parse(IO.coreSync('collect_browser_findings', cfg));
// after
const out = JSON.parse(await IO.core('collect_browser_findings', cfg)); Defensive patterns
Strategy: fallback
Validate before calling
// offscreen contexts: never use the sync accessor
const useSync = typeof document !== 'undefined' && !isOffscreenDocument;
if (!useSync) { /* route through await IO.core(...) */ } Type guard
function supportsSyncCore(io) { return typeof io.coreSync === 'function' && !io.isOffscreen; } Try / catch
let out;
try { out = JSON.parse(IO.coreSync('collect_browser_findings', cfg)); }
catch (e) {
if (String(e.message).includes('offscreen adapter is asynchronous')) {
out = JSON.parse(await IO.core('collect_browser_findings', cfg));
} else throw e;
} Prevention
- Write shared scan code against the async IO.core API only
- Tag the adapter object with its mode (live vs offscreen) and branch explicitly
- Add lint/CI checks forbidding coreSync in offscreen modules
When it happens
Trigger: Calling IO.coreSync() (or code paths that require the synchronous visual core) while running inside createOffscreenVisualIO, i.e. the offscreen scan pipeline in the Chrome extension.
Common situations: Sharing visual-contrast code between the live-page path (sync DOM available) and the offscreen path (snapshot only) and hitting the sync branch; calling coreSync from custom extensions of the scan.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- [impeccable] the offscreen document has no live page; load a
- [impeccable] detector core unavailable: ${reason} (a Content
AI-assisted analysis of pbakaus/impeccable@2bc2879276 (2026-09-08).
Data as JSON: /api/errors/317d7de0f03d5df4.
Report an issue: GitHub.