fix: make missing daemon errors actionable - #2356
Conversation
🦋 Changeset detectedLatest commit: 030c02a The changes in this PR will be included in the next version bump. This PR includes changesets to release 0 packagesWhen changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
All reported issues were addressed across 4 files
Architecture diagram
sequenceDiagram
participant Client as CLI / Test
participant DaemonClient as sendDriverRequest()
participant Socket as Unix Socket
Client->>DaemonClient: openViaDaemon(session, url)
DaemonClient->>DaemonClient: create socket connection
DaemonClient->>Socket: connect(socketPath)
Note over Socket: Daemon process not running
Socket-->>DaemonClient: 'error' event
alt Error code is ECONNREFUSED or ENOENT
DaemonClient->>DaemonClient: isDaemonUnavailableError(error) → true
DaemonClient->>DaemonClient: daemonNotRunningError(session, request)
Note over DaemonClient: Builds CommandFailure with:<br/>message includes "browse open …"<br/>resultCode = "daemon_not_running"
DaemonClient-->>Client: CommandFailure (actionable)
else Other socket error (e.g., ECONNRESET)
DaemonClient->>DaemonClient: failRequest(original error)
DaemonClient-->>Client: raw error (unchanged)
end
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Addresses feedback from Cubic
b1d217f to
1cc01b4
Compare
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Confidence score: 5/5
- In
packages/cli/src/lib/cloud/reduce-logs.ts,reduceLogslacks focused unit tests around the newly guarded CDP payload branches, so malformed or partial records (including accepted error records) could slip through and cause silent parsing/filtering regressions in log reduction output—add targeted tests for console, log-entry, response-status, and related malformed-path cases.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/cli/src/lib/cloud/reduce-logs.ts">
<violation number="1" location="packages/cli/src/lib/cloud/reduce-logs.ts:57">
P3: `reduceLogs` has no focused unit coverage for the newly guarded CDP payload paths, so malformed/partial log records and accepted error records can regress unnoticed. Add tests for console, log-entry, response-status, and invalid payload cases.
(Based on your team's feedback about unit tests for new behavior.) [FEEDBACK_USED]</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| } | ||
|
|
||
| export function reduceLogs(raw: RawLog[], opts: ReduceLogsOptions = {}): unknown[] { | ||
| export function reduceLogs( |
There was a problem hiding this comment.
P3: reduceLogs has no focused unit coverage for the newly guarded CDP payload paths, so malformed/partial log records and accepted error records can regress unnoticed. Add tests for console, log-entry, response-status, and invalid payload cases.
(Based on your team's feedback about unit tests for new behavior.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/cli/src/lib/cloud/reduce-logs.ts, line 57:
<comment>`reduceLogs` has no focused unit coverage for the newly guarded CDP payload paths, so malformed/partial log records and accepted error records can regress unnoticed. Add tests for console, log-entry, response-status, and invalid payload cases.
(Based on your team's feedback about unit tests for new behavior.) </comment>
<file context>
@@ -25,13 +40,24 @@ function paramsOf(e: RawLog): Record<string, any> {
}
-export function reduceLogs(raw: RawLog[], opts: ReduceLogsOptions = {}): unknown[] {
+export function reduceLogs(
+ raw: RawLog[],
+ opts: ReduceLogsOptions = {},
</file context>
There was a problem hiding this comment.
Addressed in 7312a6f31 -- add focused reducer coverage for console, log-entry, response-status, exception, loading-failure, and malformed payload paths, plus runtime guards for malformed console argument containers and entries.
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Confidence score: 3/5
- In
packages/cli/src/lib/driver/daemon/client.ts, the stop-race path can treat a reused PID as the same daemon, leaving stale socket/PID files behind; this can block later starts withdaemon_unresponsiveand force manual recovery (--force/cleanup) — verify daemon identity before cleanup decisions (not PID liveness alone) and ensure stale artifacts are removed on mismatch.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/cli/src/lib/driver/daemon/client.ts">
<violation number="1" location="packages/cli/src/lib/driver/daemon/client.ts:128">
P2: A reused PID can leave the dead daemon's socket/PID files behind after this stop race, because liveness does not establish that the PID is this daemon. Later starts then report `daemon_unresponsive` and require `--force`; persist and validate a daemon-specific process identity (such as creation time) before retaining these files.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
## Summary
- translate daemon socket `ECONNREFUSED` and `ENOENT` failures into a
human-readable error
- print the exact `browse open` command that restarts the requested
session
- document that API keys are forwarded to an already-running daemon and
that `browse stop` is idempotent
- preserve regression coverage for late environment variables and
exit-code-zero cleanup
## Root cause
The daemon client passed raw Unix socket errors through to users when
the daemon disappeared between the readiness check and the request.
Agents received `ECONNREFUSED` without a recovery command.
## Impact
Agents now get an actionable `daemon_not_running` failure with the exact
command needed to restart the session. Recovery command arguments are
shell-quoted, and a daemon disappearing between status and stop is
treated as an already-stopped session. Cleanup synchronizes with daemon
startup so it preserves a replacement daemon that starts during the stop
race. The bundled SKILL.md also makes the already-fixed env timing and
stop behavior explicit.
## E2E Test Matrix
| Command / flow | Observed output | Confidence / sufficiency |
| --- | --- | --- |
| `pnpm --filter browse build` | TypeScript compilation and oclif
manifest generation completed successfully. | Proves the exact local CLI
code under review builds; does not exercise a live browser. |
| `pnpm --filter browse test:cli` | 25 test files passed; 366/366 tests
passed. This includes deterministic daemon disappearance/restart races,
adversarial recovery-command shell quoting, malformed CDP log payloads,
and the complete CLI contract suite. | Proves the full CLI suite passes
against the completed local build on macOS; GitHub CI provides the
Ubuntu/Windows matrix. |
| `BROWSE_DAEMON_DIR=<temp dir> node packages/cli/bin/run.js stop
--session no-daemon-smoke` | Exited `0` and printed `{ "stopped": false,
"session": "no-daemon-smoke" }`. | Exercises the built CLI's real
absent-daemon stop path and confirms its documented successful no-op
behavior. |
| `pnpm --filter browse lint` | Full-package Prettier, ESLint, and
TypeScript checks completed successfully. | Reproduces the CI lint
pipeline locally and verifies the daemon changes, log reducer, tests,
and inherited CLI lint baseline. |
Linear:
[GRO-1908](https://linear.app/browserbase/issue/GRO-1908/cli-namespace-2027-dev-suggested-docs-updates)
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Replaces raw socket errors with a clear `daemon_not_running` message and
prints the exact, shell-quoted `browse open` recovery command. `browse
stop` now treats a missing daemon as already stopped, validates
ownership before cleanup, and avoids tearing down a daemon that restarts
mid-race. Addresses Linear:
https://linear.app/browserbase/issue/GRO-1908/cli-namespace-2027-dev-suggested-docs-updates.
- **Bug Fixes**
- Map `ECONNREFUSED`/`ENOENT` to `daemon_not_running` and show the
precise, session-aware recovery command.
- Make `browse stop` idempotent and safe: if the daemon vanished (no
`--force`), return `{ stopped: false }`; acquire a lock and verify
daemon ownership before removing pid/socket/lock; preserve a replacement
daemon started during the stop race (handles PID reuse).
- Harden CDP log reduction: stricter parsing, ignore malformed payloads,
and require numeric response statuses.
- Docs: clarify `BROWSERBASE_API_KEY` is forwarded on every command and
`browse stop` is idempotent; add troubleshooting for the new error.
- Tests: cover the error message and shell-quoted recovery command
(round-tripped through `/bin/sh`), stop race cleanup and restart
preservation, PID reuse, and reduce-logs edge cases.
<sup>Written for commit 030c02a.
Summary will update on new commits.</sup>
<a
href="https://cubic.dev/pr/browserbase/stagehand/pull/2356?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
---------
Summary
ECONNREFUSEDandENOENTfailures into a human-readable errorbrowse opencommand that restarts the requested sessionbrowse stopis idempotentRoot cause
The daemon client passed raw Unix socket errors through to users when the daemon disappeared between the readiness check and the request. Agents received
ECONNREFUSEDwithout a recovery command.Impact
Agents now get an actionable
daemon_not_runningfailure with the exact command needed to restart the session. Recovery command arguments are shell-quoted, and a daemon disappearing between status and stop is treated as an already-stopped session. Cleanup synchronizes with daemon startup so it preserves a replacement daemon that starts during the stop race. The bundled SKILL.md also makes the already-fixed env timing and stop behavior explicit.E2E Test Matrix
pnpm --filter browse buildpnpm --filter browse test:cliBROWSE_DAEMON_DIR=<temp dir> node packages/cli/bin/run.js stop --session no-daemon-smoke0and printed{ "stopped": false, "session": "no-daemon-smoke" }.pnpm --filter browse lintLinear: GRO-1908
Summary by cubic
Replaces raw socket errors with a clear
daemon_not_runningmessage and prints the exact, shell-quotedbrowse openrecovery command.browse stopnow treats a missing daemon as already stopped, validates ownership before cleanup, and avoids tearing down a daemon that restarts mid-race. Addresses Linear: https://linear.app/browserbase/issue/GRO-1908/cli-namespace-2027-dev-suggested-docs-updates.ECONNREFUSED/ENOENTtodaemon_not_runningand show the precise, session-aware recovery command.browse stopidempotent and safe: if the daemon vanished (no--force), return{ stopped: false }; acquire a lock and verify daemon ownership before removing pid/socket/lock; preserve a replacement daemon started during the stop race (handles PID reuse).BROWSERBASE_API_KEYis forwarded on every command andbrowse stopis idempotent; add troubleshooting for the new error./bin/sh), stop race cleanup and restart preservation, PID reuse, and reduce-logs edge cases.Written for commit 030c02a. Summary will update on new commits.