microsoft/playwright · error · Error
Recording is already in progress.
Error message
Recording is already in progress.
What it means
BrowserContextDispatcher allows only one active recording session per context. startRecording stores the RecorderApp in this._recorderApp; if it is already set, a programmatic recording is running and a second startRecording call is rejected. Stop the current recording first.
Solutions
- Call context.stopRecording() (or the stopRecording channel method) before starting again.
- Guard the start call in your code so startRecording is invoked at most once per context.
- Create a fresh browser context for each recording session.
Example fix
// before
await context.startRecording({ language: 'javascript' });
await context.startRecording({ language: 'javascript' });
// after
await context.startRecording({ language: 'javascript' });
await context.stopRecording();
await context.startRecording({ language: 'javascript' }); Defensive patterns
Strategy: try-catch
Try / catch
try {
await context.startRecording({ language: 'javascript' });
} catch (e) {
if (e.message.includes('Recording is already in progress')) {
await context.stopRecording();
await context.startRecording({ language: 'javascript' });
} else throw e;
} Prevention
- Track recording state in your harness and stop before every start.
- Use one browser context per recording session.
When it happens
Trigger: Calling context.startRecording() (via the BrowserContext channel) twice without calling stopRecording() in between.
Common situations: Test harnesses or tools that kick off codegen/recording per test but only stop it conditionally; retry logic re-invoking startRecording after a partial failure that left the recorder attached.
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
- Recorder is shown, close it before starting a recording.
- Recording is in progress, stop it before showing the…
- Browser context was closed before the daemon could start
- "deviceScaleFactor" option is not supported with null…
- Error: unable to connect to a browser that does not have…
AI-assisted analysis of microsoft/playwright@500c9c822c (2026-09-15).
Data as JSON: /api/errors/0ba101fde44af298.
Report an issue: GitHub.
Appendix: source
Thrown at packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts:361
async storageState(params: channels.BrowserContextStorageStateParams, progress: Progress): Promise<channels.BrowserContextStorageStateResult> {
return await this._context.storageState(progress, params);
}
async setStorageState(params: channels.BrowserContextSetStorageStateParams, progress: Progress): Promise<void> {
await this._context.setStorageState(progress, params.storageState, 'api');
}
async close(params: channels.BrowserContextCloseParams, progress: Progress): Promise<void> {
await this._context.close(progress, params);
}
async showRecorder(params: channels.BrowserContextShowRecorderParams, progress: Progress): Promise<void> {
await progress.race(RecorderApp.show(this._context, params));
}
async startRecording(params: channels.BrowserContextStartRecordingParams, progress: Progress): Promise<void> {
if (this._recorderApp)
throw new Error('Recording is already in progress.');
// Recorder events only go to the connection that started the recording.
this._recorderApp = await progress.race(ProgrammaticRecorderApp.start(this._context, params, ({ event, data, page, code }) => {
this._dispatchEvent('recorderEvent', { event, data, code, page: PageDispatcher.from(this, page) });
}));
}
async stopRecording(params: channels.BrowserContextStopRecordingParams, progress: Progress): Promise<void> {
const recorderApp = this._recorderApp;
this._recorderApp = undefined;
if (recorderApp)
await progress.race(recorderApp.stop());
}
async exposeConsoleApi(params: channels.BrowserContextExposeConsoleApiParams, progress: Progress): Promise<void> {
await this._context.exposeConsoleApi(progress);
}
async pause(params: channels.BrowserContextPauseParams, progress: Progress) {View on GitHub (pinned to 500c9c822c)