microsoft/playwright · error · Error
Video recording has already been started.
Error message
Video recording has already been started.
What it means
Thrown by Context.startVideoRecording when called while a recording is already in flight. The context holds a single _video slot that is set on start and only cleared by stopVideoRecording, so a second start is rejected as a state-machine violation.
Source
Thrown at packages/playwright-core/src/tools/backend/context.ts:219
if (!tab)
throw new Error(`Tab ${index} not found`);
const url = tab.page.url();
await tab.page.close();
return url;
}
async workspaceFile(fileName: string, perCallWorkspaceDir: string | undefined): Promise<string> {
return await workspaceFile(this.options, fileName, perCallWorkspaceDir);
}
async outputFile(template: FilenameTemplate, options: { origin: 'code' | 'llm' }): Promise<string> {
const baseName = template.suggestedFilename || `${template.prefix}-${(template.date ?? new Date()).toISOString().replace(/[:.]/g, '-')}${template.ext ? '.' + template.ext : ''}`;
return await outputFile(this.options, baseName, options);
}
async startVideoRecording(fileName: string, params: VideoParams) {
if (this._video)
throw new Error('Video recording has already been started.');
this._video = { params, fileName, fileNames: [] };
const browserContext = await this.ensureBrowserContext();
for (const page of browserContext.pages())
await this._startPageVideo(page);
}
async stopVideoRecording(): Promise<string[]> {
if (!this._video)
return [];
const video = this._video;
for (const page of this._rawBrowserContext.pages())
await page.screencast.stop();
this._video = undefined;
return [...video.fileNames];
}
private async _startPageVideo(page: playwrightTypes.Page) {
if (!this._video)View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Call context.stopVideoRecording() (or the browser_video_record_stop tool) before starting a new recording.
- Guard the call: check whether a recording is already active via the tool/context API before invoking start.
- If reusing one context across tasks, tear down video in a finally block so a failed run cannot leave _video set.
Example fix
// before
await context.startVideoRecording(file, params);
await context.startVideoRecording(file, params); // throws
// after
if (await context.isVideoRecording?.() !== true) {
await context.startVideoRecording(file, params);
} Defensive patterns
Strategy: validation
Validate before calling
// Before calling startVideoRecording, ensure no recording is active.
// If you control the Context, track recording state externally:
let videoActive = false;
async function safeStart(ctx, file, params) {
if (videoActive) await ctx.stopVideoRecording();
await ctx.startVideoRecording(file, params);
videoActive = true;
}
async function safeStop(ctx) {
if (!videoActive) return [];
videoActive = false;
return ctx.stopVideoRecording(); // safe to call when inactive (returns [])
} Type guard
// No public guard; mirror the internal state with a wrapper.
const videoState = new WeakMap<Context, boolean>();
function isVideoRecording(ctx: Context): boolean {
return videoState.get(ctx) ?? false;
} Try / catch
// stopVideoRecording is safe to call when inactive (returns []). Prefer calling it // defensively rather than try/catch on start. await context.stopVideoRecording(); // no-op if inactive await context.startVideoRecording(file, params);
Prevention
- Always pair startVideoRecording with a guaranteed stopVideoRecording in a finally block.
- Treat the recorder as a single-flight resource: one active recording per context.
- In MCP/agent flows, route video start/stop through a tool that tracks the active state.
When it happens
Trigger: Calling startVideoRecording twice without an intervening stopVideoRecording; or invoking the browser_video_record_start MCP tool a second time while the first recording is still active.
Common situations: MCP/LLM agent retrying a video-capture step without first issuing stop; an orchestration script that re-enters a 'record' code path after an error path that never stopped the previous recording.
Related errors
- Tracing is not started
- Screencast is already running
- Cannot start tracing while stopping
- Tracing has been already started
- Must start tracing before starting a new chunk
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/c8ebd82b2c4b609e.
Report an issue: GitHub.