microsoft/playwright · error · Error

No recording in progress, use ${startRecording.schema.name}

Error message

No recording in progress, use ${startRecording.schema.name} to start one.

What it means

Thrown by the stopRecording tool handler when context.stopRecording() returns falsy, meaning no recording session was ever started (or it was already stopped). It tells the caller to start a recording before trying to stop one.

Source

Thrown at packages/playwright-core/src/tools/backend/recorder.ts:54

    response.addTextResult(`Recording started. Call ${stopRecording.schema.name} to retrieve the recorded actions.`);
  },
});

const stopRecording = defineTool({
  capability: 'devtools',

  schema: {
    name: 'browser_stop_recording',
    title: 'Stop recording user actions',
    description: 'Stop the recording started with browser_start_recording and return the recorded actions as Playwright code.',
    inputSchema: z.object({}),
    type: 'readOnly',
  },

  handle: async (context, params, response) => {
    const recordedActions = await context.stopRecording();
    if (!recordedActions)
      throw new Error(`No recording in progress, use ${startRecording.schema.name} to start one.`);
    if (!recordedActions.length) {
      response.addTextResult('Recording stopped. No actions were recorded.');
    } else {
      const codeframe = codeframeForLanguage(context.codegenLanguage());
      response.addTextResult(`Recording stopped. Recorded actions:\n\n\`\`\`${codeframe}\n${recordedActions.join('\n')}\n\`\`\``);
    }
    response.setIncludeSnapshot();
  },
});

export default [
  startRecording,
  stopRecording,
];

View on GitHub (pinned to deda92d15e)

Solutions

  1. Call startRecording first and confirm it succeeded before stopping
  2. Track client-side whether a recording is active; don't call stop twice
  3. If state is unknown, treat this error as a no-op signal that no recording exists

Example fix

// before
await client.callTool({ name: 'stopRecording', arguments: {} });

// after
await client.callTool({ name: 'startRecording', arguments: {} });
// ... record actions ...
await client.callTool({ name: 'stopRecording', arguments: {} });
Defensive patterns

Strategy: validation

Validate before calling

// Track session state client-side before calling the tool
if (!recordingActive) { console.log('no recording to stop'); } else {
  await client.callTool({ name: 'stopRecording', arguments: {} });
  recordingActive = false;
}

Try / catch

try {
  await client.callTool({ name: 'stopRecording', arguments: {} });
} catch (e) {
  if ((e as Error).message.includes('No recording in progress')) return; // treat as no-op
  throw e;
}

Prevention

When it happens

Trigger: Invoking the stopRecording MCP tool before any successful startRecording call, or calling stopRecording twice (the second call returns undefined because _recordedActions was already cleared).

Common situations: An MCP agent assumes recording is on (e.g. a prior startRecording call failed on version skew) and proceeds to stop; client state desync after a reconnect; double-submit of the stop tool.

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


AI-assisted analysis of microsoft/playwright@deda92d15e (2026-08-27). Data as JSON: /api/errors/469975f598bdc20d. Report an issue: GitHub.