microsoft/playwright · error · Error

Unable to serialize OPFS: ${e.message}

Error message

Unable to serialize OPFS: ${e.message}

What it means

During storage-state collection, Playwright gathers the Origin Private File System (OPFS) contents of a page so they can be captured into storageState. If enumerating or reading the OPFS directory fails for any reason, the code in `collect` wraps the underlying error and rethrows it with this prefix so the caller knows serialization of browser storage failed. The original failure reason is preserved in the message suffix.

Source

Thrown at packages/injected/src/storageScript.ts:187

    return collect(root, '');
  }

  async collect(record: { indexedDB: boolean, opfs: boolean }): Promise<SerializedStorage> {
    const localStorage = Object.keys(this._global.localStorage).map(name => ({ name, value: this._global.localStorage.getItem(name)! }));
    const result: SerializedStorage = { localStorage };
    if (record.indexedDB) {
      try {
        const databases = await this._global.indexedDB.databases();
        result.indexedDB = await Promise.all(databases.map(db => this._collectDB(db)));
      } catch (e) {
        throw new Error('Unable to serialize IndexedDB: ' + e.message);
      }
    }
    if (record.opfs) {
      try {
        result.opfs = await this._collectOPFS(await this._global.navigator.storage.getDirectory());
      } catch (e) {
        throw new Error('Unable to serialize OPFS: ' + e.message);
      }
    }
    return result;
  }

  private async _restoreDB(dbInfo: IndexedDBDatabase) {
    const openRequest = this._global.indexedDB.open(dbInfo.name, dbInfo.version);
    openRequest.addEventListener('upgradeneeded', () => {
      const db = openRequest.result;
      for (const store of dbInfo.stores) {
        const objectStore = db.createObjectStore(store.name, { autoIncrement: store.autoIncrement, keyPath: store.keyPathArray ?? store.keyPath });
        for (const index of store.indexes)
          objectStore.createIndex(index.name, index.keyPathArray ?? index.keyPath!, { unique: index.unique, multiEntry: index.multiEntry });
      }
    });

    // after `upgradeneeded` finishes, `success` event is fired.
    const db = await this._idbRequestToPromise(openRequest);

View on GitHub (pinned to 312030cdce)

Solutions

  1. Read the underlying `e.message` after the prefix to identify the actual OPFS failure and fix that root cause.
  2. Ensure the page origin is a secure context (https:// or localhost) where `navigator.storage` is fully functional.
  3. Verify the browser context is not blocking storage (check context permissions / storage partitioning settings).
  4. Avoid mutating or navigating the page concurrently while storageState collection runs.
  5. If OPFS capture is not needed, disable OPFS recording so `record.opfs` is not set.

Example fix

// before
const state = await context.storageState({ opfs: true }); // throws if OPFS unreadable
// after
let state;
try {
  state = await context.storageState({ opfs: true });
} catch (e) {
  if (String(e.message).startsWith('Unable to serialize OPFS:')) {
    state = await context.storageState(); // fall back without OPFS
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!page.url().startsWith('https://') && !page.url().startsWith('http://localhost')) {
  // OPFS/File System Access API requires a secure context
  console.warn('Skipping OPFS capture on insecure origin:', page.url());
}

Type guard

function supportsOpfs(win: Window): boolean {
  return !!win.navigator?.storage?.getDirectory;
}

Try / catch

try {
  const state = await context.storageState({ opfs: true });
} catch (e) {
  if (String(e.message).includes('Unable to serialize OPFS:')) {
    const reason = String(e.message).replace('Unable to serialize OPFS: ', '');
    console.error('OPFS capture failed:', reason);
    // fall back to non-OPFS storage state
  } else throw e;
}

Prevention

When it happens

Trigger: A page that has `record.opfs` set (storageState collection with OPFS enabled) calls `navigator.storage.getDirectory()` and `_collectOPFS`, and either getting the directory handle or walking/reading OPFS files throws — e.g. the page is on an insecure origin, storage is partitioned/blocked, a file was deleted mid-read, or a File System Access API operation rejects.

Common situations: Running storageState capture against a page whose origin blocks persistent storage (private/incognito-like contexts, Safari ITP, cross-origin iframes with partitioned storage), or a race where OPFS entries vanish between listing and reading during parallel test teardown.

Understand the failure class

Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of microsoft/playwright@312030cdce (2026-09-07). Data as JSON: /api/errors/559b49136c5fa1a6. Report an issue: GitHub.