{"record":{"id":"9253429aafaaab21","repo":"microsoft/playwright","slug":"recorder-is-shown-close-it-before-starting-a-recording","errorCode":null,"errorMessage":"Recorder is shown, close it before starting a recording.","messagePattern":"Recorder is shown, close it before starting a recording\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/recorder/recorderApp.ts","lineNumber":367,"sourceCode":"}\n\nexport type ProgrammaticRecorderEvent = {\n  event: 'actionAdded' | 'actionUpdated' | 'signalAdded';\n  data: any;\n  page: Page;\n  code: string;\n};\n\n// Each client that records through the api gets its own app, so that clients\n// sharing the context only receive events of the recordings they started.\nexport class ProgrammaticRecorderApp {\n  private _context: BrowserContext;\n  private _recorder: Recorder;\n  private _listeners: RegisteredListener[];\n\n  static async start(context: BrowserContext, params: channels.BrowserContextStartRecordingParams, onEvent: (event: ProgrammaticRecorderEvent) => void): Promise<ProgrammaticRecorderApp> {\n    if ((context as any)[recorderAppSymbol])\n      throw new Error('Recorder is shown, close it before starting a recording.');\n    const recorder = await Recorder.forContext(context, { language: params.language, mode: 'recording', recorderMode: 'api', omitCallTracking: true });\n    const app = new ProgrammaticRecorderApp(context, recorder, params, onEvent);\n    programmaticRecorderApps(context).add(app);\n    await recorder.setMode('recording');\n    return app;\n  }\n\n  private constructor(inspectedContext: BrowserContext, recorder: Recorder, params: channels.BrowserContextStartRecordingParams, onEvent: (event: ProgrammaticRecorderEvent) => void) {\n    this._context = inspectedContext;\n    this._recorder = recorder;\n    let lastAction: actions.ActionInContext | undefined;\n    let lastActionPage: Page | undefined;\n    const languages = [...languageSet()];\n\n    // Only actions are generated, so the header options do not matter.\n    const languageGeneratorOptions = {\n      browserName: inspectedContext._browser.options.name,\n      launchOptions: { headless: false },","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/microsoft/playwright/blob/500c9c822ce7664539a4c8a88810048dfe090c3b/packages/playwright-core/src/server/recorder/recorderApp.ts#L349-L385","documentation":"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.","triggerScenarios":"Calling context.startRecording (ProgrammaticRecorderApp.start) after showRecorder has opened the interactive RecorderApp on the same context and it has not been closed.","commonSituations":"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.","solutions":["Close the interactive recorder before starting a programmatic recording.","Start the programmatic recording on a different browser context.","Reorder your workflow: do all programmatic recordings first, open the interactive recorder afterwards."],"exampleFix":"// before\nawait context.showRecorder({ mode: 'recording' });\nawait context.startRecording({ language: 'javascript' }); // throws\n// after\nawait context.close(); // or close the recorder window\nconst ctx2 = await browser.newContext();\nawait ctx2.startRecording({ language: 'javascript' });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await context.startRecording({ language: 'python' });\n} catch (e) {\n  if (e.message.includes('Recorder is shown')) {\n    // close the interactive recorder first, or use a new context\n    await context.close();\n    context = await browser.newContext();\n    await context.startRecording({ language: 'python' });\n  } else throw e;\n}","preventionTips":["Dedicate separate contexts to interactive codegen and API recording.","Close the recorder UI as part of teardown before programmatic recording starts."],"tags":["recorder","codegen","state-conflict","mutual-exclusion"],"backgroundTag":"invalid-state-transition","analyzedSha":"500c9c822ce7664539a4c8a88810048dfe090c3b","analyzedAt":"2026-09-15T07:37:15.003Z","contentChangedAt":"2026-09-15T07:37:15.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}