microsoft/playwright · error · Error

Recorder is shown, close it before starting a recording.

Error message

Recorder is shown, close it before starting a recording.

What it means

ProgrammaticRecorderApp.start refuses to begin an API-driven recording when the interactive recorder UI is already attached to the context, detected via the recorderAppSymbol on the BrowserContext. The two recorder modes are mutually exclusive per context; close the shown recorder first.

Solutions

  1. Close the interactive recorder before starting a programmatic recording.
  2. Start the programmatic recording on a different browser context.
  3. Reorder your workflow: do all programmatic recordings first, open the interactive recorder afterwards.

Example fix

// before
await context.showRecorder({ mode: 'recording' });
await context.startRecording({ language: 'javascript' }); // throws
// after
await context.close(); // or close the recorder window
const ctx2 = await browser.newContext();
await ctx2.startRecording({ language: 'javascript' });
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await context.startRecording({ language: 'python' });
} catch (e) {
  if (e.message.includes('Recorder is shown')) {
    // close the interactive recorder first, or use a new context
    await context.close();
    context = await browser.newContext();
    await context.startRecording({ language: 'python' });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling context.startRecording (ProgrammaticRecorderApp.start) after showRecorder has opened the interactive RecorderApp on the same context and it has not been closed.

Common situations: Users open codegen manually, then a script tries to start programmatic recording on the same browser context; automation that shows the recorder for inspection and later starts headless recording without closing it.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of microsoft/playwright@500c9c822c (2026-09-15). Data as JSON: /api/errors/9253429aafaaab21. Report an issue: GitHub.

Appendix: source

Thrown at packages/playwright-core/src/server/recorder/recorderApp.ts:367

}

export type ProgrammaticRecorderEvent = {
  event: 'actionAdded' | 'actionUpdated' | 'signalAdded';
  data: any;
  page: Page;
  code: string;
};

// Each client that records through the api gets its own app, so that clients
// sharing the context only receive events of the recordings they started.
export class ProgrammaticRecorderApp {
  private _context: BrowserContext;
  private _recorder: Recorder;
  private _listeners: RegisteredListener[];

  static async start(context: BrowserContext, params: channels.BrowserContextStartRecordingParams, onEvent: (event: ProgrammaticRecorderEvent) => void): Promise<ProgrammaticRecorderApp> {
    if ((context as any)[recorderAppSymbol])
      throw new Error('Recorder is shown, close it before starting a recording.');
    const recorder = await Recorder.forContext(context, { language: params.language, mode: 'recording', recorderMode: 'api', omitCallTracking: true });
    const app = new ProgrammaticRecorderApp(context, recorder, params, onEvent);
    programmaticRecorderApps(context).add(app);
    await recorder.setMode('recording');
    return app;
  }

  private constructor(inspectedContext: BrowserContext, recorder: Recorder, params: channels.BrowserContextStartRecordingParams, onEvent: (event: ProgrammaticRecorderEvent) => void) {
    this._context = inspectedContext;
    this._recorder = recorder;
    let lastAction: actions.ActionInContext | undefined;
    let lastActionPage: Page | undefined;
    const languages = [...languageSet()];

    // Only actions are generated, so the header options do not matter.
    const languageGeneratorOptions = {
      browserName: inspectedContext._browser.options.name,
      launchOptions: { headless: false },

View on GitHub (pinned to 500c9c822c)