{"record":{"id":"e69ee66fc3048412","repo":"microsoft/playwright","slug":"must-start-tracing-before-stopping","errorCode":null,"errorMessage":"Must start tracing before stopping","messagePattern":"Must start tracing before stopping","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/trace/recorder/tracing.ts","lineNumber":396,"sourceCode":"    const result = await progress.race(recorder.export(mode));\n    this.harRecorders.delete(harId || '');\n    return result;\n  }\n\n  private _closeAllGroups() {\n    while (this._currentGroupId())\n      this._groupEnd();\n  }\n\n  async stopChunk(progress: Progress, params: TracingTracingStopChunkParams): Promise<{ artifact?: Artifact, entries?: NameValue[] }> {\n    if (this._isStopping)\n      throw new Error(`Tracing is already stopping`);\n    this._isStopping = true;\n\n    if (!this._state || !this._state.recording) {\n      this._isStopping = false;\n      if (params.mode !== 'discard')\n        throw new Error(`Must start tracing before stopping`);\n      return {};\n    }\n\n    this._closeAllGroups();\n\n    this._context.instrumentation.removeListener(this);\n    eventsHelper.removeEventListeners(this._eventListeners);\n    if (this._state.options.screencast)\n      this._stopScreencast();\n    // We don't need websocket frames outside of the recording window. This also\n    // stops updating websocket content blobs, which we want to stay unchanged for zipping.\n    this._harTracer.setOmitWebSocketFrames(true);\n    if (this._state.options.snapshotDom)\n      this._snapshotter?.stop();\n\n    this.flushHarEntries();\n\n    // Network file survives across chunks, make a snapshot before returning the resulting entries.","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/trace/recorder/tracing.ts#L378-L414","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nafterEach(async () => {\n  await context.tracing.stopChunk({ path: 'trace.zip' }); // throws if never started\n});\n\n// after\nafterEach(async () => {\n  if (tracingStarted) {\n    await context.tracing.stopChunk({ path: 'trace.zip' });\n  } else {\n    await context.tracing.stopChunk({ mode: 'discard' }).catch(() => {});\n  }\n});","handlingStrategy":"validation","validationCode":"let tracingActive = false;\nasync function guardedStop(ctx, out) {\n  if (!tracingActive) {\n    return ctx.tracing.stopChunk({ mode: 'discard' }).catch(() => ({}));\n  }\n  tracingActive = false;\n  return ctx.tracing.stopChunk({ path: out });\n}","typeGuard":null,"tryCatchPattern":"try { await context.tracing.stopChunk({ path: out }); }\ncatch (e) {\n  if (/Must start tracing before stopping/i.test(String(e.message))) return;\n  throw e;\n}","preventionTips":["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."],"tags":["tracing","state-machine","lifecycle"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}