microsoft/playwright · error · Error

Tracing has been already started

Error message

Tracing has been already started

What it means

Tracing.start() throws when an active trace already exists (this._state is set). Tracing is single-instance per context: you must fully stop an existing trace before starting a new one. This protects against overlapping trace writers that would share the same trace/network files.

Source

Thrown at packages/playwright-core/src/server/trace/recorder/tracing.ts:154

  }

  private _sdkLanguage() {
    return this._context instanceof BrowserContext ? this._context._browser.sdkLanguage() : this._context.attribution.playwright.options.sdkLanguage;
  }

  async resetForReuse(progress: Progress) {
    // Discard previous chunk if any and ignore any errors there.
    await this.stopChunk(progress, { mode: 'discard' }).catch(() => {});
    await progress.race(this._stop());
    if (this._snapshotter)
      await progress.race(this._snapshotter.resetForReuse());
  }

  start(progress: Progress, options: TracerOptions) {
    if (this._isStopping)
      throw new Error('Cannot start tracing while stopping');
    if (this._state)
      throw new Error('Tracing has been already started');

    // Re-write for testing.
    this._contextCreatedEvent.sdkLanguage = this._sdkLanguage();

    // TODO: passing the same name for two contexts makes them write into a single file
    // and conflict.
    const traceName = options.name || createGuid();

    const tracesDir = this._createTracesDirIfNeeded();

    // Init the state synchronously.
    this._state = {
      options,
      traceName,
      tracesDir,
      traceFile: path.join(tracesDir, traceName + '.trace'),
      networkFile: path.join(tracesDir, traceName + '.network'),
      chunkOrdinal: 0,

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Guard with tracing.stop() before start, or check that tracing isn't already started.
  2. Remove the duplicate start call — let only the test runner's trace config own tracing.
  3. Use a fresh browser context per trace-owned test so _state cannot carry over.

Example fix

// before
await context.tracing.start({ snapshots: true });
await context.tracing.start({ screenshots: true }); // already started

// after
await context.tracing.start({ snapshots: true, screenshots: true });
// ... or stop first:
await context.tracing.stop();
await context.tracing.start({ screenshots: true });
Defensive patterns

Strategy: validation

Validate before calling

async function startOnce(ctx, opts) {
  try {
    await ctx.tracing.start(opts);
  } catch (e) {
    if (!/already been started/i.test(String(e.message))) throw e;
    // already started; optionally stop and restart with new options
    await ctx.tracing.stop();
    await ctx.tracing.start(opts);
  }
}

Try / catch

try { await context.tracing.start(opts); }
catch (e) {
  if (/already been started/i.test(String(e.message))) {
    await context.tracing.stop();
    await context.tracing.start(opts);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling context.tracing.start(...) twice in a row without context.tracing.stop() between them; calling start when the test runner already enabled tracing for the project.

Common situations: A beforeEach hook enables tracing and the test body also calls tracing.start; two suites sharing a browser context both try to own tracing; forgetting to call tracing.stop() at the end of a previous run.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/eebcc9dd0394bd3f. Report an issue: GitHub.