← Back to microsoft/playwright
microsoft / playwright · Issue No. 42742
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.
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.
All six responses are completely consumed by the page. For each, Playwright should emit requestfinished, and host-side response.json() should return {"ok":true}.
Both no-store + stream repetitions:
done: true, parses all 11 bytes and returns ok: true.requestfailed with net::ERR_ABORTED.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.
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.
Manually allowlisted environment fields (no environment-variable dump):
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.