openclaw/openclaw · error · Error

Failed to open tab (non-selectable target)

Error message

Failed to open tab (non-selectable target)

What it means

Plain Error thrown at server-context.tab-ops.ts:433 when the freshly created CDP target fails isSelectableCdpBrowserTarget, i.e. created.type !== "page" OR the resolved URL (created.url ?? requested url) starts with a browser-internal scheme (chrome://, chrome-untrusted://, devtools://, edge://, brave://, vivaldi://, opera://). The runtime will not hand internal/devtools targets to user-facing automation.

Source

Thrown at extensions/browser/src/browser/server-context.tab-ops.ts:433

    ).catch(async (err: unknown) => {
      if (String(err).includes("HTTP 405")) {
        return await fetchJson<CdpTarget>(
          endpoint,
          cdpActionTimeouts?.httpTimeoutMs ?? CDP_JSON_NEW_TIMEOUT_MS,
          undefined,
          getCdpControlPolicy(),
        );
      }
      throw err;
    });

    opts?.signal?.throwIfAborted();
    if (!created.id) {
      throw new Error("Failed to open tab (missing id)");
    }
    const resolvedUrl = created.url ?? url;
    if (!isSelectableCdpBrowserTarget({ url: resolvedUrl, type: created.type })) {
      throw new Error("Failed to open tab (non-selectable target)");
    }
    await assertBrowserNavigationResultAllowed({ url: resolvedUrl, ...ssrfPolicyOpts });
    const wsUrl = normalizeWsUrl(created.webSocketDebuggerUrl, profile.cdpUrl);
    const committedUrl = wsUrl
      ? await waitForCdpCommittedNavigationUrl({
          wsUrl,
          configuredCdpUrl: profile.cdpUrl,
          cdpPolicy: getCdpControlPolicy(),
          requestedUrl: url,
          signal: opts?.signal,
          timeouts: cdpActionTimeouts,
        })
      : undefined;
    opts?.signal?.throwIfAborted();
    if (!committedUrl) {
      return await withTabOwnership(
        {
          targetId: created.id,

View on GitHub (pinned to 01804a7531)

Solutions

  1. Open only real web URLs (http/https) or about:blank; do not pass chrome://, devtools://, or other internal schemes to openTab.
  2. If a redirect lands on an internal page, navigate to the final web URL directly instead of relying on the redirect.
  3. If you genuinely need an internal page, use the CDP target API directly rather than the selectable tab path — but most internal pages are intentionally non-selectable.

Example fix

// before
await openTab({ url: "chrome://settings" });

// after
await openTab({ url: "https://example.com" });
Defensive patterns

Strategy: validation

Validate before calling

import { isSelectableCdpBrowserTarget } from "./cdp-target-filter.js";

const INTERNAL = /^(chrome|chrome-untrusted|devtools|edge|brave|vivaldi|opera):\/\//i;
function isOpenableUrl(url) {
  return /^https?:|^about:blank$/i.test(url) && !INTERNAL.test(url);
}
if (!isOpenableUrl(url)) {
  // reject before calling openTab
}

Type guard

function isOpenableTabUrl(url) {
  return typeof url === "string" && /^(https?:|about:blank$)/i.test(url) &&
    !/^(chrome|chrome-untrusted|devtools|edge|brave|vivaldi|opera):/i.test(url);
}

Prevention

When it happens

Trigger: Calling openTab with a chrome://, devtools://, or edge:// URL; or the CDP /json/new response yields a target whose type is not "page" (e.g. "background_page", "service_worker", "browser"). resolvedUrl falls back to the requested url when created.url is absent, so passing an internal scheme directly triggers it.

Common situations: A user/agent tries to open chrome://settings or devtools:// to automate browser config; a URL shortener/redirect lands on a browser-internal page; opening a URL that Chrome resolves to a non-page target type. Also seen when the requested url is empty and Chrome creates a service-worker target.

Related errors


AI-assisted analysis of openclaw/openclaw@01804a7531 (2026-08-12). Data as JSON: /api/errors/4df731361cb6beb7. Report an issue: GitHub.