microsoft/playwright · error · Error
No recording in progress
Error message
No recording in progress
What it means
Thrown by the dashboard page-tab controller's `stopRecording` when `_recordingPath` is null. `_recordingPath` is set by `startRecording` and cleared by `stopRecording`, so calling stop without a prior start, or calling stop twice, triggers it.
Source
Thrown at packages/playwright-core/src/tools/dashboard/dashboardController.ts:681
async keydown(params: { key: string }) {
await wrapInternal(this._page, () => this._page.keyboard.down(params.key));
}
async keyup(params: { key: string }) {
await wrapInternal(this._page, () => this._page.keyboard.up(params.key));
}
async startRecording() {
const artifactsDir = this._owner.artifactsDirFor(this._page.context());
this._recordingPath = path.join(artifactsDir, `recording-${Date.now()}.webm`);
if (this._screencastRunning)
await this._restartScreencast(this._page);
}
async stopRecording(): Promise<{ streamId: string }> {
const p = this._recordingPath;
if (!p)
throw new Error('No recording in progress');
this._recordingPath = null;
if (this._screencastRunning)
await this._restartScreencast(this._page);
const handle = await fs.promises.open(p, 'r');
const streamId = crypto.randomUUID();
this._owner._streams.set(streamId, { handle, path: p });
return { streamId };
}
async screenshot(): Promise<{ data: string; viewportWidth: number; viewportHeight: number; ariaSnapshot: string }> {
const buffer = await wrapInternal(this._page, () => this._page.screenshot({ type: 'png' }));
const ariaSnapshot = await wrapInternal(this._page, () => this._page.ariaSnapshot({ boxes: true, mode: 'ai' }));
const vp = await this._viewportSize();
return {
data: buffer.toString('base64'),
viewportWidth: vp.width,
viewportHeight: vp.height,
ariaSnapshot,View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Guard the stop call: only invoke `stopRecording` when a recording is actually in progress (track state in the caller).
- Avoid double-stop — disable the stop control after the first click until a new recording starts.
- If `startRecording` can fail, ensure the UI's recording flag is reset on failure so stop is not attempted.
Example fix
// before
controller.stopRecording(); // may throw
// after
if (isRecording) {
const { streamId } = await controller.stopRecording();
isRecording = false;
} Defensive patterns
Strategy: validation
Validate before calling
let recording = false;
async function safeStart() { if (!recording) { await controller.startRecording(); recording = true; } }
async function safeStop() {
if (!recording) return null;
const r = await controller.stopRecording(); recording = false;
return r;
} Try / catch
try {
await controller.stopRecording();
} catch (e) {
if (!/No recording in progress/.test((e as Error).message)) throw e;
// otherwise no-op
} Prevention
- Track an isRecording flag in the UI/caller and reset it on start success and stop.
- Disable the stop control when no recording is active.
- Reset the flag if startRecording throws so stop is never attempted.
When it happens
Trigger: Calling `stopRecording` before `startRecording`; calling `stopRecording` a second time after the first call already nulled `_recordingPath`; a UI button (stop) wired to stop without checking recording state.
Common situations: Dashboard user clicks stop on a tab where recording was never started; a UI state desync where the stop button is enabled but no recording is active; an error during `startRecording` left `_recordingPath` unset but the UI believes a recording is running.
Related errors
- Unknown stream: ${params.streamId}
- The object has been collected to prevent unbounded heap grow
- Cannot find object to "${method}": ${guid}
- Unknown new child: ${params.guid}
- Cannot find parent object ${parentGuid} to create ${guid}
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/272b6e576e7c1311.
Report an issue: GitHub.