{"record":{"id":"272b6e576e7c1311","repo":"microsoft/playwright","slug":"no-recording-in-progress","errorCode":null,"errorMessage":"No recording in progress","messagePattern":"No recording in progress","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/tools/dashboard/dashboardController.ts","lineNumber":681,"sourceCode":"  async keydown(params: { key: string }) {\n    await wrapInternal(this._page, () => this._page.keyboard.down(params.key));\n  }\n\n  async keyup(params: { key: string }) {\n    await wrapInternal(this._page, () => this._page.keyboard.up(params.key));\n  }\n\n  async startRecording() {\n    const artifactsDir = this._owner.artifactsDirFor(this._page.context());\n    this._recordingPath = path.join(artifactsDir, `recording-${Date.now()}.webm`);\n    if (this._screencastRunning)\n      await this._restartScreencast(this._page);\n  }\n\n  async stopRecording(): Promise<{ streamId: string }> {\n    const p = this._recordingPath;\n    if (!p)\n      throw new Error('No recording in progress');\n    this._recordingPath = null;\n    if (this._screencastRunning)\n      await this._restartScreencast(this._page);\n    const handle = await fs.promises.open(p, 'r');\n    const streamId = crypto.randomUUID();\n    this._owner._streams.set(streamId, { handle, path: p });\n    return { streamId };\n  }\n\n  async screenshot(): Promise<{ data: string; viewportWidth: number; viewportHeight: number; ariaSnapshot: string }> {\n    const buffer = await wrapInternal(this._page, () => this._page.screenshot({ type: 'png' }));\n    const ariaSnapshot = await wrapInternal(this._page, () => this._page.ariaSnapshot({ boxes: true, mode: 'ai' }));\n    const vp = await this._viewportSize();\n    return {\n      data: buffer.toString('base64'),\n      viewportWidth: vp.width,\n      viewportHeight: vp.height,\n      ariaSnapshot,","sourceCodeStart":663,"sourceCodeEnd":699,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/tools/dashboard/dashboardController.ts#L663-L699","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncontroller.stopRecording(); // may throw\n// after\nif (isRecording) {\n  const { streamId } = await controller.stopRecording();\n  isRecording = false;\n}","handlingStrategy":"validation","validationCode":"let recording = false;\nasync function safeStart() { if (!recording) { await controller.startRecording(); recording = true; } }\nasync function safeStop() {\n  if (!recording) return null;\n  const r = await controller.stopRecording(); recording = false;\n  return r;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await controller.stopRecording();\n} catch (e) {\n  if (!/No recording in progress/.test((e as Error).message)) throw e;\n  // otherwise no-op\n}","preventionTips":["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."],"tags":["dashboard","recording","lifecycle","state-management"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}