jackwener/OpenCLI · error · BrowserConnectError

profile-required

profile-required

Error message

Multiple Browser Bridge profiles are connected

What it means

ensureBrowserBridgeReady found the daemon health in state 'profile-required', meaning multiple Browser Bridge profiles/contexts are connected and the caller must specify which one to use. The library throws BrowserConnectError with code profile-required because it cannot disambiguate the target context.

Source

Thrown at src/browser/daemon-lifecycle.ts:143

    }

    if (!portReleased) {
      throw new BrowserConnectError(
        'Stale daemon could not be replaced',
        `A stale daemon (${reason}) is running but did not shut down (graceful + SIGKILL both failed).\n` +
        '  Run manually: opencli daemon stop',
        'daemon-not-running',
      );
    }
    staleDaemonReplaced = true;
  }

  if (!staleDaemonReplaced && health.state === 'ready') {
    return health;
  }

  if (!staleDaemonReplaced && health.state === 'profile-required') {
    throw browserConnectErrorFromHealth(health, contextId);
  }

  if (staleDaemonReplaced || health.state === 'stopped') {
    if (verbose && (process.env.OPENCLI_VERBOSE || process.stderr.isTTY)) {
      process.stderr.write('⏳ Starting daemon...\n');
    }
    daemonLifecycleHooks.spawnDaemonProcess();
  } else if (verbose && (process.env.OPENCLI_VERBOSE || process.stderr.isTTY)) {
    process.stderr.write('⏳ Waiting for Chrome/Chromium extension to connect...\n');
    process.stderr.write('   Make sure Chrome or Chromium is open and the OpenCLI extension is enabled.\n');
  }

  const finalHealth = await waitForBridgeReady(getDaemonHealth, { timeoutMs, contextId, preferredContextId });
  if (finalHealth.state === 'ready') return finalHealth;
  throw browserConnectErrorFromHealth(finalHealth, contextId);
}

function browserConnectErrorFromHealth(health: DaemonHealth, contextId?: string): BrowserConnectError {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Pass an explicit context/profile ID to the command so the bridge knows which connected profile to target.
  2. Close or disconnect the extra Chrome profiles/windows running the OpenCLI extension.
  3. Restart the daemon so only one profile reconnects, then rerun the command.
  4. Check daemon health output to list connected profiles and pick the correct contextId.

Example fix

// before
await runCommand('browser', 'click', { selector: '#go' });
// after: disambiguate the profile
await runCommand('browser', 'click', { selector: '#go', contextId: 'work-profile' });
Defensive patterns

Strategy: validation

Validate before calling

const health = await bridge.health();
if (health.state === 'profile-required') {
  // supply a contextId before running commands
}

Type guard

function isProfileRequired(health) {
  return health.state === 'profile-required';
}

Try / catch

try {
  await runCommand('browser', action, args);
} catch (e) {
  if (e.code === 'profile-required') {
    return runCommand('browser', action, { ...args, contextId: pickProfileId() });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling any Browser Bridge-backed command (via _ensureDaemon or health) when the daemon reports more than one connected profile and no contextId/preferredContextId was supplied; the code path browserConnectErrorFromHealth(health, contextId) converts the health state into this error.

Common situations: Multiple Chrome profiles (personal + work) running the OpenCLI extension, several browser windows across profiles connected simultaneously, or leftover daemon connections from a previous session.

Related errors


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