diff --git a/src/McpContext.ts b/src/McpContext.ts index a3bcddbbc..315ef3bee 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -450,13 +450,20 @@ export class McpContext implements Context { } const node = this.#textSnapshot?.idToNode.get(uid); if (!node) { - throw new Error('No such element found in the snapshot'); + throw new Error('No such element found in the snapshot.'); } - const handle = await node.elementHandle(); - if (!handle) { - throw new Error('No such element found in the snapshot'); + const message = `Element with uid ${uid} no longer exists on the page.`; + try { + const handle = await node.elementHandle(); + if (!handle) { + throw new Error(message); + } + return handle; + } catch (error) { + throw new Error(message, { + cause: error, + }); } - return handle; } /** diff --git a/src/tools/input.ts b/src/tools/input.ts index f5d789187..381b19855 100644 --- a/src/tools/input.ts +++ b/src/tools/input.ts @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import {logger} from '../logger.js'; import type {McpContext, TextSnapshotNode} from '../McpContext.js'; import {zod} from '../third_party/index.js'; import type {ElementHandle} from '../third_party/index.js'; @@ -22,6 +23,16 @@ const includeSnapshotSchema = zod .optional() .describe('Whether to include a snapshot in the response. Default is false.'); +function handleActionError(error: unknown, uid: string) { + logger('failed to act using a locator', error); + throw new Error( + `Failed to interact with the element with uid ${uid}. The element did not become interactive within the configured timeout.`, + { + cause: error, + }, + ); +} + export const click = defineTool({ name: 'click', description: `Clicks on the provided element`, @@ -55,6 +66,8 @@ export const click = defineTool({ if (request.params.includeSnapshot) { response.includeSnapshot(); } + } catch (error) { + handleActionError(error, uid); } finally { void handle.dispose(); } @@ -119,6 +132,8 @@ export const hover = defineTool({ if (request.params.includeSnapshot) { response.includeSnapshot(); } + } catch (error) { + handleActionError(error, uid); } finally { void handle.dispose(); } @@ -180,6 +195,8 @@ async function fillFormElement( value.length * timeoutPerChar; await handle.asLocator().setTimeout(fillTimeout).fill(value); } + } catch (error) { + handleActionError(error, uid); } finally { void handle.dispose(); }