Skip to content

[Bug]: browserContext.setStorageState() never settles when the state contains IndexedDB and a page of the context is open on that origin #42258

Description

@Snooz82

Version

1.62.1

Steps to reproduce

browserContext.storageState({ indexedDB: true }) leaves an open IndexedDB connection in the page it reads from. That connection then blocks any later attempt to delete or recreate the database on that origin — including the one browserContext.setStorageState() performs internally. The result is a promise that never settles and never times out.

Two independent symptoms follow from the same leaked connection:

  1. After storageState({ indexedDB: true }), indexedDB.deleteDatabase() in the page fires onblocked instead of onsuccess.
  2. setStorageState() with a state containing IndexedDB hangs forever while a page of the context is open on that origin. It is not affected by context.setDefaultTimeout().

Reproduction (Chromium, macOS; any origin works — the snippet uses example.com):

const { chromium } = require('playwright');

const SEED = async () => {
  const req = indexedDB.open('rfdb', 1);
  req.onupgradeneeded = () => req.result.createObjectStore('kv');
  const db = await new Promise((res, rej) => {
    req.onsuccess = () => res(req.result);
    req.onerror = () => rej(req.error);
  });
  await new Promise((res, rej) => {
    const tx = db.transaction('kv', 'readwrite');
    tx.objectStore('kv').put('token-abc', 'auth');
    tx.oncomplete = () => res();
    tx.onerror = () => rej(tx.error);
  });
  db.close();
};

const DELETE = async () => {
  await new Promise((res, rej) => {
    const req = indexedDB.deleteDatabase('rfdb');
    req.onsuccess = () => res();
    req.onerror = () => rej(req.error);
    req.onblocked = () => rej(new Error('blocked'));
  });
};

(async () => {
  const browser = await chromium.launch();

  // Symptom 1: storageState({indexedDB}) leaves a connection open.
  for (const withSave of [false, true]) {
    const context = await browser.newContext();
    const page = await context.newPage();
    await page.goto('https://example.com/');
    await page.evaluate(SEED);
    if (withSave) await context.storageState({ indexedDB: true });
    let result = 'ok';
    try {
      await page.evaluate(DELETE);
    } catch {
      result = 'BLOCKED';
    }
    console.log(`storageState({indexedDB}) called first = ${withSave} -> deleteDatabase ${result}`);
    await context.close();
  }

  // Symptom 2: setStorageState() never settles, and ignores the context timeout.
  const context = await browser.newContext();
  context.setDefaultTimeout(3000);
  const page = await context.newPage();
  await page.goto('https://example.com/');
  await page.evaluate(SEED);
  const state = await context.storageState({ indexedDB: true });
  const outcome = await Promise.race([
    context.setStorageState(state).then(() => 'resolved', (e) => 'rejected: ' + e.message),
    new Promise((res) => setTimeout(() => res('still pending after 20s'), 20000)),
  ]);
  console.log(`setStorageState -> ${outcome}`);
  process.exit(0);
})();

Expected behavior

storageState({ indexedDB: true }) closes the IndexedDB connections it opens, so that the page it read from is left exactly as it was found. setStorageState() restores the state regardless of whether pages of the context are open on the affected origin, and — if it genuinely cannot proceed — rejects within the context timeout rather than hanging.

Actual behavior

storageState({indexedDB}) called first = false -> deleteDatabase ok
storageState({indexedDB}) called first = true  -> deleteDatabase BLOCKED
setStorageState -> still pending after 20s

The promise returned by setStorageState() never settles. context.setDefaultTimeout(3000) has no effect on it.

Additional context

Workarounds

Anything that drops the page's IndexedDB connections before setStorageState() makes it resolve, and the data is then restored correctly:

Between storageState() and setStorageState() Result
nothing hangs
page.reload() resolves, value restored
page.goto('about:blank') resolves, value restored
page.close() resolves, value restored
restore into a different, freshly created context resolves, value restored

Environment

  • Playwright: 1.62.1
  • Browser: Chromium (bundled)
  • OS: macOS 15 (Darwin 25.5.0), arm64
  • Node.js: v22.20.0

Environment

System:
    OS: macOS 26.5.1
    CPU: (12) arm64 Apple M2 Max
    Memory: 71.11 MB / 32.00 GB
  Binaries:
    Node: 22.20.0 - /Users/XXX/.nvm/versions/node/v22.20.0/bin/node
    Yarn: 1.22.22 - /Users/XXX/.nvm/versions/node/v22.20.0/bin/yarn
    npm: 11.6.2 - /Users/XXX/.nvm/versions/node/v22.20.0/bin/npm
    pnpm: 10.30.3 - /Users/XXX/.nvm/versions/node/v22.20.0/bin/pnpm
  IDEs:
    VSCode: 1.132.0 - /usr/local/bin/code
    Claude Code: 2.1.233 - /Users/XXX/.local/bin/claude
    Codex: 0.147.0 - /Users/XXX/.local/bin/codex
  Languages:
    Bash: 3.2.57 - /bin/bash
  npmPackages:
    playwright: ^1.62.1 => 1.62.1

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions