Uh oh!
There was an error while loading. Please reload this page.
fix(tracing): allow restarting tracing after a failed stop - #42425
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| // Stop tracing even when saving the trace failed, otherwise tracing can | ||
| // never be started again on this context. | ||
| let error: Error | undefined = await this._doStopChunk(options.path).catch(e => e); | ||
| await this._channel.tracingStop({}, kNoTimeout).catch(e => error ??= e); |
There was a problem hiding this comment.
this still leaves tracing stuck if _doStopChunk() fails before stopping the chunk because tracingStop() rejects while the chunk is recording
There was a problem hiding this comment.
Fixed — server-side stop() now discards a still-recording chunk before stopping, so tracingStop no longer rejects. Covered by "should stop tracing when the chunk was not stopped".
| await this._channel.tracingStop({}, kNoTimeout); | ||
| // Stop tracing even when saving the trace failed, otherwise tracing can | ||
| // never be started again on this context. | ||
| let error: Error | undefined = await this._doStopChunk(options.path).catch(e => e); |
There was a problem hiding this comment.
if writing the trace archive fails, its client call stack state is not removed, so later traces continue appending calls to an abandoned trace record
There was a problem hiding this comment.
Fixed — _doStopChunk now releases the stack session when saving fails. Covered by "should release the stack session when saving the trace fails".
| let error: Error | undefined = await this._doStopChunk(options.path).catch(e => e); | ||
| await this._channel.tracingStop({}, kNoTimeout).catch(e => error ??= e); | ||
| if (error) | ||
| throw error; |
There was a problem hiding this comment.
NIT: unless we specifically care about exposing an error from _doStopChunk over tracingStop then we could just do this
| throwerror; | |
| try{ | |
| awaitthis._doStopChunk(options.path); | |
| }finally{ | |
| awaitthis._channel.tracingStop({},kNoTimeout); | |
| } |
There was a problem hiding this comment.
We do want the save error to surface, and with the server-side discard tracingStop is not expected to throw anymore, so this is now const error = await ...catch(e => e); await tracingStop(); if (error) throw error; — same shape as browserContext.close().
Dmitry Gozman (dgozman)
commented
Aug 27, 2026
I agree with Devin, if we want to make |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Hi, I'm the Playwright bot and I took a first pass at the CI failures here. 🔴 The failures are caused by this PR — its own new tracing tests fail on every browserThe latest "tests 1" run (33130700334) has 16 failures, and every one is a test this PR adds — DetailsOverall: one real, PR-introduced failure story — the two recovery tests this PR adds fail everywhere. No other true failures in this run. Worst call is red. Caused by this PR
Pre-existing flake / infra
Triaged by the Playwright bot - agent run |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| } | ||
| } | ||
| private async _stopChunk(progress: Progress, params: TracingTracingStopChunkParams): Promise<{ artifact?: Artifact, entries?: NameValue[] }> { |
There was a problem hiding this comment.
Perhaps we can make this method sync now? It has a single await that we can probably lift up to stopChunk.
There was a problem hiding this comment.
Done — _stopChunk is sync now, the _fs.sync() await and result building moved into stopChunk.
| test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/42423' }); | ||
| await context.tracing.start(); | ||
| await page.goto(server.PREFIX + '/input/button.html'); | ||
| // Saving the chunk can fail before it was stopped, leaving the server recording. |
There was a problem hiding this comment.
Can we trigger this through public APIs instead of calling private methods on the channel?
There was a problem hiding this comment.
You are right that it drives the protocol out of order — the sequence is not reachable through public APIs in a regular client (only a thin client can fail before stopping the chunk). Dropped the test; the server-side recovery in stop() stays.
When tracing.stop() failed to save the chunk (e.g. a trace file write errored), tracingStop was never sent, so the server kept its recording state and every later tracing.start() on that context threw "Tracing has been already started" for the rest of the context lifetime. Fixes: microsoft#42423
Addresses review feedback: stop() must leave tracing in a clean state even when it throws, so that a subsequent start() works. - server: stop() discards a chunk the client failed to stop, instead of rejecting with "Must stop trace file before stopping tracing". - server: stopChunk() always releases the recording state. - client: release the stack session when saving the trace fails, otherwise later traces keep appending calls to the abandoned session. - client: delete the artifact when saving it fails.
- server stop() unconditionally discards the chunk (a no-op when not recording), resetForReuse() delegates to it. - client: deleting the artifact no longer masks the saveAs error. - test: stack session test does not need a page.
1eada8a to
a3b14e4CompareThe test drove the protocol out of order by calling private channel methods; the sequence is not reachable through public APIs in a regular client. The server-side recovery in stop() stays.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Test results for "tests 1"5 flaky51264 passed, 1240 skipped Merge workflow run. |
Test results for "MCP"1 failed 8258 passed, 1361 skipped Merge workflow run. |
Hi, I'm the Playwright bot and I took a look at the failing CI here. 🟢 CI is clear — the one failure is a pre-existing flakeThe only red job is windows-latest - firefox, on DetailsPre-existing flake / infra
No other jobs failed — the rest of the matrix (chromium/webkit/firefox across Linux/macOS/Windows, all Test Runner shards, installation and lint jobs) passed. Triaged by the Playwright bot - agent run |
a30296c
into
microsoft:mainUh oh!
There was an error while loading. Please reload this page.
Summary
When
tracing.stop()failed to save the trace (e.g. a disk write error), tracing on that context was left permanently wedged: every latertracing.start()threw "Tracing has been already started" andstop()threw "Must start tracing before stopping", with no recovery API. A failed stop also leaked the client-side stack session, so later traces kept appending calls to it.stop()always sendstracingStop, then surfaces the save error.stop()discards a chunk the client failed to stop;stopChunk()always releases the recording state.Fixes#42423