fix(browser): improve browser control disabled/error hints#34864
fix(browser): improve browser control disabled/error hints#34864zhangjunmengyang wants to merge 1 commit intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR improves the browser control error-hint UX in two ways: it avoids the misleading "Can't reach the service" wrapper when browser control is simply disabled in config, and it softens the operator hint to check enablement/running status before recommending a full gateway restart (reducing the risk of restart loops on intermittent failures). A small unit test is added to lock both behaviors.
Confidence Score: 5/5
Last reviewed commit: c9424f3 |
| if (msgLower.includes("browser control disabled")) { | ||
| return new Error( | ||
| `OpenClaw browser control is disabled. Enable it in config before using the browser tool. ${modelHint}`, | ||
| ); | ||
| } |
There was a problem hiding this comment.
Fragile string-based detection couples two locations
The detection here (msgLower.includes("browser control disabled")) is tightly coupled to the literal string thrown at line 185 (new Error("browser control disabled")). If either string drifts independently (e.g. one is updated to "browser control is disabled" to match the user-facing message), the special-case branch will silently stop firing and fall through to the generic "Can't reach" wrapper — precisely the misleading path this PR is trying to avoid.
Using a shared constant or a dedicated error subclass (similar to BrowserServiceError) would make the coupling explicit and type-safe:
// top of file
const BROWSER_CONTROL_DISABLED_MSG = "browser control disabled" as const;
// line 185
throw new Error(BROWSER_CONTROL_DISABLED_MSG);
// line 118
if (msgLower.includes(BROWSER_CONTROL_DISABLED_MSG)) { ... }Or, even more robust — a subclass like BrowserControlDisabledError extends Error that can be caught by instanceof (just as BrowserServiceError is handled at line 261).
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/browser/client-fetch.ts
Line: 118-122
Comment:
**Fragile string-based detection couples two locations**
The detection here (`msgLower.includes("browser control disabled")`) is tightly coupled to the literal string thrown at line 185 (`new Error("browser control disabled")`). If either string drifts independently (e.g. one is updated to `"browser control is disabled"` to match the user-facing message), the special-case branch will silently stop firing and fall through to the generic "Can't reach" wrapper — precisely the misleading path this PR is trying to avoid.
Using a shared constant or a dedicated error subclass (similar to `BrowserServiceError`) would make the coupling explicit and type-safe:
```typescript
// top of file
const BROWSER_CONTROL_DISABLED_MSG = "browser control disabled" as const;
// line 185
throw new Error(BROWSER_CONTROL_DISABLED_MSG);
// line 118
if (msgLower.includes(BROWSER_CONTROL_DISABLED_MSG)) { ... }
```
Or, even more robust — a subclass like `BrowserControlDisabledError extends Error` that can be caught by `instanceof` (just as `BrowserServiceError` is handled at line 261).
How can I resolve this? If you propose a fix, please make it concise.| it("surfaces 'browser control disabled' as a config issue (not reachability)", async () => { | ||
| await expect(fetchBrowserJson("/")).rejects.toThrow(/browser control is disabled/i); | ||
| }); |
There was a problem hiding this comment.
Test doesn't assert absence of the old "Can't reach" wrapper
The PR description explicitly calls out eliminating the misleading "Can't reach the ... service" wrapper for the disabled case, but the test only asserts the presence of the new message — it never verifies the old wrapper is gone. A future regression that re-introduces the wrapper (e.g. the msgLower.includes check stops matching) would still pass this test.
Consider adding a negative assertion:
| it("surfaces 'browser control disabled' as a config issue (not reachability)", async () => { | |
| await expect(fetchBrowserJson("/")).rejects.toThrow(/browser control is disabled/i); | |
| }); | |
| await expect(fetchBrowserJson("/")).rejects.toThrow(/browser control is disabled/i); | |
| await expect(fetchBrowserJson("/")).rejects.not.toThrow(/can't reach/i); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/browser/client-fetch.error-hints.test.ts
Line: 42-44
Comment:
**Test doesn't assert absence of the old "Can't reach" wrapper**
The PR description explicitly calls out eliminating the misleading "Can't reach the ... service" wrapper for the disabled case, but the test only asserts the presence of the new message — it never verifies the old wrapper is gone. A future regression that re-introduces the wrapper (e.g. the `msgLower.includes` check stops matching) would still pass this test.
Consider adding a negative assertion:
```suggestion
await expect(fetchBrowserJson("/")).rejects.toThrow(/browser control is disabled/i);
await expect(fetchBrowserJson("/")).rejects.not.toThrow(/can't reach/i);
```
How can I resolve this? If you propose a fix, please make it concise.c9424f3 to
bcd7584
Compare
|
This is a great UX improvement. I've been bitten by the misleading "can't reach service" error when browser control was simply disabled. One thought: for Browserbase users, it might also be helpful to hint at rate limit scenarios (e.g., "max concurrent sessions reached") separately from "service unreachable." We hit 429s from Browserbase that surface as generic browser failures. Not blocking this PR, just a potential follow-up. Happy to open a separate issue if useful. |
Summary
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
Security Impact (required)
No)No)No)No)No)Yes, explain risk + mitigation: N/ARepro + Verification
Environment
Steps
fetchBrowserJson("/")while browser control is disabled.fetchBrowserJson("http://127.0.0.1:18888/", { timeoutMs: 5 })with an aborting timeout.Expected
Actual
Evidence
Local unit test:
Human Verification (required)
Compatibility / Migration
Yes)No)No)Failure Recovery (if this breaks)
src/browser/client-fetch.tsRisks and Mitigations
CI note
Current CI failure on this PR appears unrelated to code changes:
oven-sh/setup-bun@v2attempts to downloadbun-v1.3.9+cf6cdbbbaand receives HTTP 404.