microsoft/playwright · error · Error
Must start tracing before stopping
Error message
Must start tracing before stopping
What it means
Tracing.stopChunk() throws when there is no active/recording state AND the requested mode is not 'discard'. A non-discard stopChunk (entries/archive export) requires that tracing was actually started and a chunk recorded; calling it on an idle tracer means there is nothing to export.
Source
Thrown at packages/playwright-core/src/server/trace/recorder/tracing.ts:396
const result = await progress.race(recorder.export(mode));
this.harRecorders.delete(harId || '');
return result;
}
private _closeAllGroups() {
while (this._currentGroupId())
this._groupEnd();
}
async stopChunk(progress: Progress, params: TracingTracingStopChunkParams): Promise<{ artifact?: Artifact, entries?: NameValue[] }> {
if (this._isStopping)
throw new Error(`Tracing is already stopping`);
this._isStopping = true;
if (!this._state || !this._state.recording) {
this._isStopping = false;
if (params.mode !== 'discard')
throw new Error(`Must start tracing before stopping`);
return {};
}
this._closeAllGroups();
this._context.instrumentation.removeListener(this);
eventsHelper.removeEventListeners(this._eventListeners);
if (this._state.options.screencast)
this._stopScreencast();
// We don't need websocket frames outside of the recording window. This also
// stops updating websocket content blobs, which we want to stay unchanged for zipping.
this._harTracer.setOmitWebSocketFrames(true);
if (this._state.options.snapshotDom)
this._snapshotter?.stop();
this.flushHarEntries();
// Network file survives across chunks, make a snapshot before returning the resulting entries.View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Only call stopChunk with an export path when you know tracing was started and a chunk is open.
- Use mode: 'discard' for the teardown path that must be safe when nothing was recorded.
- Guard the export call behind a flag you set in startChunk.
Example fix
// before
afterEach(async () => {
await context.tracing.stopChunk({ path: 'trace.zip' }); // throws if never started
});
// after
afterEach(async () => {
if (tracingStarted) {
await context.tracing.stopChunk({ path: 'trace.zip' });
} else {
await context.tracing.stopChunk({ mode: 'discard' }).catch(() => {});
}
}); Defensive patterns
Strategy: validation
Validate before calling
let tracingActive = false;
async function guardedStop(ctx, out) {
if (!tracingActive) {
return ctx.tracing.stopChunk({ mode: 'discard' }).catch(() => ({}));
}
tracingActive = false;
return ctx.tracing.stopChunk({ path: out });
} Try / catch
try { await context.tracing.stopChunk({ path: out }); }
catch (e) {
if (/Must start tracing before stopping/i.test(String(e.message))) return;
throw e;
} Prevention
- Only export when you know a chunk was started and is open.
- Use mode:'discard' for safe teardown that may run when nothing was recorded.
- Set/clear a flag around startChunk/stopChunk in your harness.
When it happens
Trigger: Calling context.tracing.stopChunk({ path: 'x.zip' }) (default mode 'archive') or mode 'entries' before tracing.start() / startChunk(); calling export after tracing has already stopped and cleared _state.
Common situations: A conditional afterEach that always tries to export a trace even when tracing was disabled for that test; orchestrating chunks without first starting; calling stopChunk twice (second has no state).
Related errors
- Must start tracing before starting a new chunk
- Must stop trace file before stopping tracing
- Cannot start tracing while stopping
- Tracing has been already started
- Cannot start a trace chunk while stopping
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/e69ee66fc3048412.
Report an issue: GitHub.