jackwener/OpenCLI · error · CommandExecutionError

Mercury returned malformed click result

Error message

Mercury returned malformed click result

What it means

clickText() runs a page.evaluate() script in the browser to find and click a button/link matching the given labels, then validates the returned payload. The library throws this CommandExecutionError when the evaluate() result is an object but its `clicked` field is not a boolean, i.e. the browser-side script returned an unexpected shape instead of { clicked: boolean, ... }. This guards against silently acting on malformed automation results from a changed or broken page environment.

Source

Thrown at clis/mercury/utils.js:165

}

export async function clickText(page, labels) {
    const result = await page.evaluate(`(() => {
        const labels = ${JSON.stringify(labels)};
        const norm = (s) => String(s || '').replace(/\\s+/g, ' ').trim().toLowerCase();
        const wanted = labels.map(norm);
        const candidates = Array.from(document.querySelectorAll('button, a, [role="button"], [role="link"]'))
          .filter((node) => {
            const style = window.getComputedStyle(node);
            return style.visibility !== 'hidden' && style.display !== 'none' && node.offsetParent !== null;
          });
        const el = candidates.find((node) => wanted.includes(norm(node.innerText || node.textContent || '')));
        if (!el) return { clicked: false, labels };
        el.click();
        return { clicked: true, text: el.innerText || el.textContent || '' };
    })()`);
    const payload = assertObject(result, 'click result');
    if (typeof payload.clicked !== 'boolean') throw new CommandExecutionError('Mercury returned malformed click result');
    return payload;
}

export async function clickCreateExpenseButton(page) {
    const result = await page.evaluate(`(() => {
        const norm = (s) => String(s || '').replace(/\\s+/g, ' ').trim().toLowerCase();
        const wanted = new Set(['submit expense', 'new expense']);
        const candidates = Array.from(document.querySelectorAll('button, a, [role="button"], [role="link"]'))
          .filter((node) => {
            const style = window.getComputedStyle(node);
            return style.visibility !== 'hidden' && style.display !== 'none' && node.offsetParent !== null;
          })
          .filter((node) => wanted.has(norm(node.innerText || node.textContent || '')));
        const dangerous = candidates.find((node) => {
          const container = node.closest('[role="dialog"], dialog, form, [aria-modal="true"]');
          const context = String(container?.innerText || container?.textContent || '').replace(/\\s+/g, ' ').trim();
          return Boolean(container) || /Review|receipt|amount|merchant|category|notes|expense date/i.test(context);
        });

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log the raw `result` from page.evaluate() before the throw and check its shape for the `clicked` key.
  2. Ensure the page is fully loaded and not navigated mid-evaluate; re-run inspectMercury() then retry clickText().
  3. Update to a matching version of the mercury CLI utils so the evaluate script and its validation agree.
  4. Catch CommandExecutionError and retry with a fresh page if it is transient.

Example fix

// before
const payload = assertObject(result, 'click result');
if (typeof payload.clicked !== 'boolean') throw new CommandExecutionError('Mercury returned malformed click result');
// after
const payload = assertObject(result, 'click result');
if (typeof payload.clicked !== 'boolean') {
  console.error('click result was', JSON.stringify(result));
  throw new CommandExecutionError(`Mercury returned malformed click result: ${JSON.stringify(result)}`);
}
Defensive patterns

Strategy: type-guard

Validate before calling

const payload = await page.evaluate(`(() => { ... })()`);
if (payload && typeof payload === 'object' && typeof payload.clicked === 'boolean') {
  await clickText(page, ['Save']);
}

Type guard

function isClickResult(v) {
  return v != null && typeof v === 'object' && typeof v.clicked === 'boolean';
}

Try / catch

try {
  const res = await clickText(page, ['Save']);
} catch (err) {
  if (err instanceof CommandExecutionError && /malformed click result/.test(err.message)) {
    // re-inspect page state and retry on a fresh evaluate
  } else throw err;
}

Prevention

When it happens

Trigger: page.evaluate() returns an object lacking a boolean `clicked` property — e.g. the injected script was altered, an older/cached script version is running, or the page context returned a proxy/non-serializable value that deserialized differently.

Common situations: Automating a Mercury page whose DOM/context changed so evaluate returns an unexpected shape; custom page wrappers or patched evaluate implementations that transform results; browser extensions injecting scripts that interfere with evaluate return values.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/4be181734b0c4074. Report an issue: GitHub.