microsoft/playwright · error

kCoverageStashError + ': ' + error

Error message

kCoverageStashError + ': ' + error

What it means

Playwright's coverage integration stashes per-session coverage data in browser localStorage and transfers it to the test runner. If the in-page script hit an error while collecting coverage, it records the message under a stash-error key; takeCoverageStashes then reads, removes, and re-throws it on the Node side wrapped with the kCoverageStashError prefix so page-side coverage failures surface instead of being silently dropped.

Solutions

  1. Read the text after the coverage-stash-error prefix — it is the original in-page error — and fix the underlying page-side coverage failure it reports.
  2. Stop the application from clearing/rewriting all localStorage during tests while coverage is active.
  3. Reproduce with coverage disabled to confirm the failure is coverage-specific, then narrow to the offending script.
  4. Upgrade Playwright — stash-key handling has changed between versions.

Example fix

// before (app code under test)
window.addEventListener('unload', () => localStorage.clear());
// after
window.addEventListener('unload', () => {
  Object.keys(localStorage)
    .filter(k => !k.startsWith('__playwright_coverage_stash'))
    .forEach(k => localStorage.removeItem(k));
});
Defensive patterns

Strategy: try-catch

Validate before calling

// Before running coverage-dependent assertions:
const errKey = Object.keys(localStorage).find(k => k.includes('coverage') && k.includes('error'));
if (errKey) console.warn('Pending coverage stash error:', localStorage.getItem(errKey));

Type guard

function hasCoverageStashError(storage: Storage, sessionId: string): boolean {
  return storage.getItem(`coverage-stash-error-${sessionId}`) !== null;
}

Try / catch

let coverage;
try {
  coverage = await takeCoverageStashes(sessionIds);
} catch (e) {
  console.warn('Coverage collection failed; continuing without it:', (e as Error).message);
  coverage = null;
}

Prevention

When it happens

Trigger: Calling takeCoverageStashes (via the coverage/chunks integration) when the browser page's injected coverage script previously stored an error string under stashErrorKey(sessionId) in localStorage.

Common situations: App code clears or rewrites localStorage during the test (logout handlers, storage migrations) and clobbers coverage stash entries; multiple contexts sharing an origin collide on stash keys; the in-page collector throws on unusual script content.

Related errors


AI-assisted analysis of microsoft/playwright@f1d33b5029 (2026-09-21). Data as JSON: /api/errors/2923d0835efbfa32. Report an issue: GitHub.

Appendix: source

Thrown at packages/injected/src/coverageScript.ts:120

}

function stashErrorKey(sessionId: string) {
  return kCoverageStashPrefix + 'error.' + sessionId;
}

// Stashes of other sessions are stale, e.g. left by a previous run in a persistent profile.
export function takeCoverageStashes(global: typeof globalThis, sessionId: string): string[] {
  const result: string[] = [];
  let storage: Storage;
  try {
    storage = global.localStorage;
  } catch {
    return result;
  }
  const error = storage.getItem(stashErrorKey(sessionId));
  if (error) {
    storage.removeItem(stashErrorKey(sessionId));
    throw new Error(kCoverageStashError + ': ' + error);
  }
  const sessionPrefix = kCoverageStashPrefix + sessionId + '.';
  const keys: string[] = [];
  for (let i = 0; i < storage.length; i++) {
    const key = storage.key(i);
    if (key && key.startsWith(kCoverageStashPrefix))
      keys.push(key);
  }
  for (const key of keys) {
    const json = key.startsWith(sessionPrefix) ? storage.getItem(key) : undefined;
    storage.removeItem(key);
    if (json)
      result.push(json);
  }
  return result;
}

View on GitHub (pinned to f1d33b5029)