Files
jackwener__opencli/docs/guide/troubleshooting.md
jakevin 3f1a723b5c fix(daemon): SIGKILL fallback when stale daemon refuses graceful shutdown (#1861)
* fix(daemon): SIGKILL fallback when stale daemon refuses graceful shutdown

When the CLI detects a stale daemon (`daemonVersion !== PKG_VERSION` after
`npm install -g @jackwener/opencli@latest`), it currently asks the daemon to
exit via `POST /shutdown` and waits up to 3 seconds for the port to release.
If the old daemon hangs, refuses /shutdown, or the endpoint is missing
entirely (pre-shutdown-endpoint version), the port stays held and the user
sees `Stale daemon could not be replaced` with a `opencli daemon stop` hint.

99% of "I just upgraded and have to run `opencli doctor` every time" reports
land here: the user upgraded the CLI but the persistent daemon survived from
a previous install, and graceful shutdown is unreliable across versions.

This patch reads the stale daemon's pid from its existing `/status` response
(daemon.ts:252 already exposes `pid: process.pid`) and falls back to
`process.kill(pid, 'SIGKILL')` after graceful shutdown fails, then waits
another 2s for the port to release. Cross-platform: Node's
`process.kill(_, 'SIGKILL')` maps to `TerminateProcess` on Windows, so no
`taskkill` shell-out is needed.

The user-visible "Stale daemon could not be replaced" error only fires when
both graceful shutdown AND SIGKILL fail (cross-user owner / cross-machine
PID — neither is reachable from a normal CLI invocation anyway). The hint
message is updated to reflect that.

Adds 2 tests:
- SIGKILL succeeds → bridge proceeds past the stale block (and eventually
  fails the no-extension wait, proving the stale branch was passed cleanly).
- SIGKILL throws EPERM AND waitForDaemonStop still returns false → falls
  through to the existing stale-daemon error.

Troubleshooting docs note the new auto-fallback.

* address opus review nits

- bridge.ts: move `await waitForDaemonStop(2000)` out of the try/catch so the
  port poll always runs after `process.kill`, even when the kill itself throws
  ESRCH (target already dead) or EPERM (cross-user owner).
- browser.test.ts: bump the existing 3 stale-daemon test fixtures from
  `pid: 1` to `pid: 999999` so the new SIGKILL fallback no longer fires a
  signal at init when the older tests reach the fallback path.
- browser.test.ts: mock `waitForDaemonStop` in those 3 tests too, since the
  real implementation now polls for 2s in the fallback path (test runtime
  was up to ~6s before; back to ~400ms).
2026-06-05 19:20:10 +08:00

2.3 KiB

Troubleshooting

Common Issues

"Extension not connected"

  • Ensure the opencli Browser Bridge extension is installed and enabled in chrome://extensions.
  • Run opencli doctor to diagnose connectivity.

Empty data or 'Unauthorized' error

  • Your login session in Chrome might have expired. Open a normal Chrome tab, navigate to the target site, and log in or refresh the page.
  • Some sites have geographic restrictions (e.g., Bilibili, Zhihu from outside China).

Browser command opens the page but still cannot read context

  • A healthy Browser Bridge connection does not guarantee that the current page target exposes the data your adapter expects.
  • Some browser adapters are sensitive to the active host or page context.
  • Example: opencli 1688 item may fail with did not expose product context if the target is too broad.
  • Retry on a real item page, refresh the page in Chrome, and if needed narrow the target, for example:
OPENCLI_CDP_TARGET=detail.1688.com opencli 1688 item 841141931191 -f json

Node API errors

  • Make sure you are using Node.js >= 20. Run node --version to verify.

Daemon issues

# View extension logs
curl localhost:19825/logs

# Stop the daemon
opencli daemon stop

# Full diagnostics
opencli doctor

The daemon is persistent and stays alive until explicitly stopped (opencli daemon stop) or the package is uninstalled.

When the CLI detects a stale daemon (version mismatch after npm install -g @jackwener/opencli@latest), it first asks the daemon to shut down via /shutdown, then falls back to SIGKILL if the daemon does not release the port within 3 seconds. Manual opencli daemon stop is only needed if SIGKILL itself is rejected (cross-user owner / cross-machine PID file).

Desktop adapter connection issues

For Electron/CDP-based adapters (Cursor, Codex, etc.):

  1. Make sure the app is launched with --remote-debugging-port=XXXX
  2. Verify the endpoint is set: echo $OPENCLI_CDP_ENDPOINT
  3. Test the endpoint: curl http://127.0.0.1:XXXX/json/version

Build errors

# Clean rebuild
rm -rf dist/
npm run build

# Type check
npx tsc --noEmit

Getting Help

  • GitHub Issues — Bug reports and feature requests
  • Run opencli doctor for comprehensive diagnostics