Skip to content

[Bug]: Chromium no-store Fetch stream reaches EOF but request fails with ERR_ABORTED #42742

Description

@etonedu2026

Version

1.63.0 (matching bundled full Chromium 153.0.8010.12). The same script also reproduces with playwright-core 1.62.1 / Chromium 151.0.7922.34.

Steps to reproduce

Repository: https://github.com/etonedu2026/playwright-no-store-stream-repro

The repository must remain private under the account owner's policy; maintainers will not have access by default. I understand the template requests a repository. To make this report independently reproducible without access, the complete synthetic script and exact dependency are included below. It contains no application code or application data.

In an empty directory, save the following as package.json:

{
  "name": "playwright-no-store-stream-repro",
  "private": true,
  "type": "module",
  "devDependencies": { "playwright-core": "1.63.0" }
}

Save the following as repro.mjs:

import { createServer } from 'node:http';
import { chromium } from 'playwright-core';

const server = createServer((req, res) => {
  const url = new URL(req.url, 'http://localhost');
  if (url.pathname !== '/data') return res.end('<html>synthetic reproduction</html>');
  res.writeHead(200, {
    'Content-Type': 'application/json',
    'Cache-Control': url.searchParams.get('cache'),
  });
  res.write('{"ok":');
  setTimeout(() => res.end('true}'), 20);
});
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
const origin = `http://127.0.0.1:${server.address().port}`;
const browser = await chromium.launch({ headless: true, executablePath: chromium.executablePath() });
const results = [];
try {
  for (const [cache, reader] of [['no-store', 'stream'], ['no-store', 'json'], ['no-cache', 'stream']]) {
    for (let repeat = 0; repeat < 2; repeat++) {
      const context = await browser.newContext();
      try {
        const page = await context.newPage();
        await page.goto(origin);
        const matches = request => new URL(request.url()).pathname === '/data';
        const terminal = new Promise(resolve => {
          page.on('requestfinished', request => {
            if (matches(request)) resolve({ event: 'requestfinished' });
          });
          page.on('requestfailed', request => {
            if (matches(request)) resolve({ event: 'requestfailed', failure: request.failure()?.errorText });
          });
        });
        const headers = page.waitForResponse(response => matches(response.request()));
        const application = page.evaluate(async ({ cache, reader }) => {
          const response = await fetch(`/data?cache=${cache}`);
          if (reader === 'json') return (await response.json()).ok;
          const handle = response.body.getReader();
          const chunks = [];
          while (true) {
            const { done, value } = await handle.read();
            if (done) break;
            chunks.push(...value);
          }
          handle.releaseLock();
          return JSON.parse(new TextDecoder().decode(new Uint8Array(chunks))).ok;
        }, { cache, reader });
        const response = await headers;
        let hostBodyOk = false;
        let hostError = null;
        try { hostBodyOk = (await response.json()).ok === true; }
        catch (error) {
          hostError = error.message.includes('No data found for resource with given identifier')
            ? 'Network.getResponseBody: No data found for resource with given identifier'
            : 'Other response body error';
        }
        results.push({ cache, reader, repeat, pageBodyOk: await application, hostBodyOk, hostError, terminal: await terminal });
      } finally { await context.close(); }
    }
  }
  console.log(JSON.stringify({ node: process.version, browser: browser.version(), results }, null, 2));
} finally {
  await browser.close();
  server.closeAllConnections();
  await new Promise(resolve => server.close(resolve));
}

Run on Linux:

npm install --ignore-scripts --no-audit --no-fund --registry=https://registry.npmjs.org
export PLAYWRIGHT_BROWSERS_PATH="$(mktemp -d)"
npx playwright-core install chromium
timeout 30s node repro.mjs

The repository pins the same version and includes a lockfile, so a repository checkout can use npm ci.

Expected behavior

All six responses are completely consumed by the page. For each, Playwright should emit requestfinished, and host-side response.json() should return {"ok":true}.

Actual behavior

Both no-store + stream repetitions:

  • Page reader reaches done: true, parses all 11 bytes and returns ok: true.
  • Playwright emits requestfailed with net::ERR_ABORTED.
  • Host response.json() fails with Network.getResponseBody: No data found for resource with given identifier.

Both no-store + native response.json() controls and both no-cache + stream controls emit requestfinished and allow host body reading. The same six-case result was observed on both versions listed above.

The process exits 0 after printing the diagnostic results; exit 0 is not a passing assertion.

Additional context

This uses only a synthetic loopback Node HTTP server and Playwright. There is no AbortController, reader.cancel, request interception, response mocking, Service Worker, application framework, account or database. Each page/context is closed only after the page promise and original request terminal event have completed.

The server's 20 ms delay separates two writes; it is not a client-side sleep intended to fix or hide a failure. Full Chromium is launched using chromium.executablePath(). Changing to no-cache is only a diagnostic control, not a proposed workaround for sensitive responses that must retain no-store.

I have not established the upstream implementation cause or whether it belongs to Chromium or Playwright. No application workaround is being claimed.

Environment

Manually allowlisted environment fields (no environment-variable dump):

  • OS: Ubuntu 26.04 LTS, Linux x86_64 under WSL2
  • Kernel: 6.18.33.1-microsoft-standard-WSL2
  • Node: 22.23.2
  • npm: 10.9.8
  • playwright-core: 1.63.0, exact npm dependency
  • Browser: bundled Chromium 153.0.8010.12, headless, full executable

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