jackwener/OpenCLI · error · CommandExecutionError

App launched but CDP not available on port ${port} after ${P

Error message

App launched but CDP not available on port ${port} after ${POLL_TIMEOUT_MS / 1000}s

What it means

After launching the Electron app, pollForReady probes the Chrome DevTools Protocol port (probeCDP) every POLL_INTERVAL_MS until POLL_TIMEOUT_MS elapses. If CDP never answers within that window, it assumes the debug port never opened and throws this CommandExecutionError.

Source

Thrown at src/launcher.ts:347

export function electronLaunchArgs(port: number, extraArgs: string[] = []): string[] {
  return [
    `--remote-debugging-port=${port}`,
    '--remote-allow-origins=*',
    ...extraArgs,
  ];
}

function manualElectronLaunchHint(label: string, port: number): string {
  return `Start ${label} manually with --remote-debugging-port=${port} --remote-allow-origins=*, then either:`;
}

async function pollForReady(port: number): Promise<void> {
  const deadline = Date.now() + POLL_TIMEOUT_MS;
  while (Date.now() < deadline) {
    if (await probeCDP(port, 1_000)) return;
    await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
  }
  throw new CommandExecutionError(
    `App launched but CDP not available on port ${port} after ${POLL_TIMEOUT_MS / 1000}s`,
    'The app may be slow to start. Try running the command again.',
  );
}

/**
 * Main entry point: resolve an Electron app to a CDP endpoint URL.
 *
 * Returns the endpoint URL: http://127.0.0.1:{port}
 */
export async function resolveElectronEndpoint(site: string): Promise<string> {
  const app = getElectronApp(site);
  if (!app) {
    throw new CommandExecutionError(
      `No Electron app registered for site "${site}"`,
      'Register the app in ~/.opencli/apps.yaml or check the site name.',
    );
  }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the command — a slow-starting app may simply need more time.
  2. Quit any already-running instance of the app first so the launched instance honors the debug port.
  3. Verify the app is launched with --remote-debugging-port (check the custom binary/wrapper in apps.yaml doesn't strip arguments).
  4. Check that nothing (firewall, VPN, security software) blocks connections to localhost:<port>.

Example fix

// before: app already running without debug port
$ myapp-cli open   # times out: CDP not available
// after
$ pkill -f MyApp && myapp-cli open
Defensive patterns

Strategy: retry

Validate before calling

const isOpen = await probeCDP(port, 1000).catch(() => false);
if (!isOpen) console.warn(`CDP port ${port} not open before launch; ensure no stale app instance is running`);

Type guard

function isCdpReachable(port: number): boolean {
  return fetch(`http://127.0.0.1:${port}/json/version`)
    .then((r) => r.ok)
    .catch(() => false) as unknown as boolean;
}

Try / catch

for (let attempt = 0; attempt < 3; attempt++) {
  try {
    const endpoint = await resolveElectronEndpoint(port);
    break;
  } catch (e) {
    if (e instanceof CommandExecutionError && /CDP not available/.test(e.message) && attempt < 2) {
      await new Promise((r) => setTimeout(r, 2000));
      continue;
    }
    throw e;
  }
}

Prevention

When it happens

Trigger: resolveElectronEndpoint launches the app with --remote-debugging-port=<port>, the process starts, but probeCDP(port) keeps failing until the timeout expires (e.g. app ignores the debug flag, port blocked, or app takes longer than POLL_TIMEOUT_MS to open the listener).

Common situations: Slow machine or heavy app startup exceeding the timeout; app was already running without the debug flag so the new instance defers to it; firewall/security software blocking localhost connections; a wrapper or custom binary that drops the --remote-debugging-port argument.

Understand the failure class

Related errors


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