microsoft/playwright · error · Error

Either time, text or textGone must be provided

Error message

Either time, text or textGone must be provided

What it means

Thrown by the browser_wait tool when none of params.time, params.text, params.textGone is provided. All three are .optional() in the schema, so zod does not enforce 'one required' — the runtime check does. At least one wait target must be supplied.

Source

Thrown at packages/playwright-core/src/tools/backend/wait.ts:37

const wait = defineTool({
  capability: 'core',

  schema: {
    name: 'browser_wait_for',
    title: 'Wait for',
    description: 'Wait for text to appear or disappear or a specified time to pass',
    inputSchema: z.object({
      time: z.number().optional().describe('The time to wait in seconds'),
      text: z.string().optional().describe('The text to wait for'),
      textGone: z.string().optional().describe('The text to wait for to disappear'),
    }),
    type: 'assertion',
  },

  handle: async (context, params, response) => {
    if (!params.text && !params.textGone && !params.time)
      throw new Error('Either time, text or textGone must be provided');

    if (params.time) {
      response.addCode(`await new Promise(f => setTimeout(f, ${params.time!} * 1000));`);
      await new Promise(f => setTimeout(f, Math.min(30000, params.time! * 1000)));
    }

    const tab = context.currentTabOrDie();
    const locator = params.text ? tab.page.getByText(params.text).first() : undefined;
    const goneLocator = params.textGone ? tab.page.getByText(params.textGone).first() : undefined;

    if (goneLocator) {
      response.addCode(`await page.getByText(${JSON.stringify(params.textGone)}).first().waitFor({ state: 'hidden' });`);
      await goneLocator.waitFor({ state: 'hidden', ...tab.actionTimeoutOptions });
    }

    if (locator) {
      response.addCode(`await page.getByText(${JSON.stringify(params.text)}).first().waitFor({ state: 'visible' });`);
      await locator.waitFor({ state: 'visible', ...tab.actionTimeoutOptions });

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Pass params.time (seconds, capped at 30s by the tool) for a fixed wait.
  2. Pass params.text to wait for text to appear, or params.textGone to wait for it to disappear.
  3. If you only need a programmatic sleep, prefer page.waitForTimeout directly rather than the tool.

Example fix

// before
await client.callTool('browser_wait', {}); // throws

// after
await client.callTool('browser_wait', { time: 2 });
// or
await client.callTool('browser_wait', { text: 'Loaded' });
Defensive patterns

Strategy: validation

Validate before calling

function validateWaitParams(p: { time?: number; text?: string; textGone?: string }) {
  const provided = [p.time, p.text, p.textGone].filter(v => v !== undefined && v !== '').length;
  if (provided === 0) throw new Error('browser_wait requires time, text, or textGone');
  if (p.time !== undefined && (typeof p.time !== 'number' || p.time < 0))
    throw new Error('time must be a non-negative number (seconds)');
}

Type guard

function hasWaitTarget(p: { time?: unknown; text?: unknown; textGone?: unknown }): boolean {
  return (
    (typeof p.time === 'number' && p.time >= 0) ||
    (typeof p.text === 'string' && p.text.length > 0) ||
    (typeof p.textGone === 'string' && p.textGone.length > 0)
  );
}

Try / catch

// Prefer validation; the empty-object case is a caller bug, not a runtime condition.
if (!hasWaitTarget(params)) {
  params = { time: 1 }; // safe default
}
await client.callTool('browser_wait', params);

Prevention

When it happens

Trigger: Invoking browser_wait with an empty object, or with only unrelated/typo fields (e.g. timeout instead of time).

Common situations: Agent calling browser_wait as a generic 'pause' without specifying a reason; field-name confusion between time (seconds) and timeout; defaulting to {} for optional params.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/e5b9cc69c5543a55. Report an issue: GitHub.