jackwener/OpenCLI · error · TargetError

${resolution.message}

Error message

${resolution.message}

What it means

When resolving a target reference inside the page (via injected JS), the in-page resolver can fail with a coded TargetError. This code re-raises the in-page failure as a TargetError carrying code, message, hint, candidates, and match count. The thrown message is exactly the resolver's resolution.message.

Source

Thrown at src/cli.ts:1026

  $ opencli browser work unbind
`);
  const originalBrowserDescription = browser.description();

  /**
   * Resolve a `<target>` (numeric ref or CSS selector) via the unified resolver.
   * Returns the CSS match count so callers can propagate `matches_n` into the
   * JSON envelope printed back to the agent.
   */
  async function resolveRef(
    page: Awaited<ReturnType<typeof getBrowserPage>>,
    ref: string,
    opts: ResolveOptions = {},
  ): Promise<{ matches_n: number; match_level: TargetMatchLevel }> {
    const resolution = await page.evaluate(resolveTargetJs(ref, opts)) as
      | { ok: true; matches_n: number; match_level: TargetMatchLevel }
      | { ok: false; code: TargetErrorCode; message: string; hint: string; candidates?: string[]; matches_n?: number };
    if (!resolution.ok) {
      throw new TargetError({
        code: resolution.code,
        message: resolution.message,
        hint: resolution.hint,
        candidates: resolution.candidates,
        matches_n: resolution.matches_n,
      });
    }
    return { matches_n: resolution.matches_n, match_level: resolution.match_level };
  }

  /**
   * Parse `--nth <n>` flag, returning the parsed 0-based index or a usage error.
   * The surface mirrors `--depth` etc. in `browser get html --as json`: the flag
   * is optional, must be a non-negative integer when present, and on failure we
   * emit the structured error envelope rather than throwing past the command.
   */
  function parseNthFlag(raw: unknown): number | null | { error: string } {
    if (raw === undefined || raw === null || raw === '') return null;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read resolution.hint and candidates in the full TargetError to see near-misses and tighten the ref
  2. Wait for the element to exist (wait-for / polling) before resolving
  3. Make the target ref unambiguous (use a more specific selector or index)
  4. Retry after page load completes — transient DOM states cause resolution failures

Example fix

// before
await browser.click('button submit'); // ambiguous: two submit buttons
// after
await browser.click('button submit [data-testid=checkout]');
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check the element exists before resolving the target
const exists = await page.$(selector);
if (!exists) throw new Error(`Target not in DOM yet: ${ref}`);

Type guard

function isFailedResolution(r: unknown): r is { ok: false; code: string; message: string; hint: string; candidates?: string[] } {
  return typeof r === 'object' && r !== null && (r as { ok?: unknown }).ok === false;
}

Try / catch

try {
  await browser.click(ref);
} catch (e) {
  if (e instanceof TargetError) {
    console.error(`Target ${ref} failed (${e.code}): ${e.message}\nHint: ${e.hint}`);
    if (e.candidates?.length) console.error('Near matches:', e.candidates);
  }
  throw e;
}

Prevention

When it happens

Trigger: page.evaluate(resolveTargetJs(ref, opts)) returns { ok:false, ... } — i.e. the target reference could not be resolved: no matching element, ambiguous matches, unsupported selector, etc., depending on the TargetErrorCode.

Common situations: Selector typo or element not present in DOM at evaluation time; ambiguous text/role refs matching multiple elements; iframe/shadow-DOM content the resolver cannot see; page navigated away before evaluation.

Related errors


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