fix: preserve underlying error message in input tool actions (#2792)

`handleActionError` previously rewrote every
`click`/`hover`/`fill`/`fill_form` failure as "The element did not
become interactive within the configured timeout", discarding the real
cause. It now keeps that wording only for an actual Puppeteer
`TimeoutError` and surfaces the underlying message otherwise, so e.g.
selecting a missing option now reports `Could not find option with text
"..."`.
This commit is contained in:
Nikolay Vitkov
2026-09-21 16:19:26 +00:00
committed by GitHub
parent 941f82a3ec
commit 6e47dbb560
3 changed files with 43 additions and 2 deletions
+1
View File
@@ -51,6 +51,7 @@ export {
KnownDevices,
CDPSessionEvent,
ScreenRecorder,
TimeoutError,
} from 'puppeteer-core';
export {default as puppeteer} from 'puppeteer-core';
export type * from 'puppeteer-core';
+8 -2
View File
@@ -5,7 +5,7 @@
*/
import type {McpContext} from '../McpContext.js';
import {zod} from '../third_party/index.js';
import {TimeoutError, zod} from '../third_party/index.js';
import type {ElementHandle, KeyInput} from '../third_party/index.js';
import type {TextSnapshotNode} from '../types.js';
import {parseKey} from '../utils/keyboard.js';
@@ -35,8 +35,14 @@ const submitKeySchema = zod
function handleActionError(error: unknown, uid: string) {
logger?.('failed to act using a locator', error);
const reason =
error instanceof TimeoutError
? 'The element did not become interactive within the configured timeout.'
: error instanceof Error
? error.message
: String(error);
throw new Error(
`Failed to interact with the element with uid ${uid}. The element did not become interactive within the configured timeout.`,
`Failed to interact with the element with uid ${uid}. ${reason}`,
{
cause: error,
},
+34
View File
@@ -588,6 +588,40 @@ describe('input', () => {
});
});
it('reports why filling out a select failed', async () => {
await withMcpContext(async (response, context, args) => {
const page = context.getSelectedMcpPage().pptrPage;
await page.setContent(
html`<select
><option value="v1">one</option
><option value="v2">two</option></select
>`,
);
context.getSelectedMcpPage().textSnapshot = await TextSnapshot.create(
context.getSelectedMcpPage(),
);
await assert.rejects(
() =>
fill(args).handler(
{
params: {
// Options are matched by their text, not their value.
uid: '1_1',
value: 'v2',
},
page: context.getSelectedMcpPage(),
},
response,
context,
),
{
message:
'Failed to interact with the element with uid 1_1. Could not find option with text "v2"',
},
);
});
});
it('fills out a select option with an empty value by text', async () => {
await withMcpContext(async (response, context, args) => {
const page = context.getSelectedMcpPage().pptrPage;