fix(browser): surface 429 rate limit errors with actionable hints#40491
fix(browser): surface 429 rate limit errors with actionable hints#40491altaywtf merged 6 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR improves observability when Browserbase returns HTTP 429 (max concurrent sessions exceeded) by surfacing actionable, no-retry error messages instead of the previous generic "Browser control failed" text. The change touches three layers of the browser stack — the HTTP/dispatcher fetch client, the CDP helper used for WebSocket-endpoint discovery, and the Playwright session retry loop — and adds four test cases covering both fetch paths. Key changes:
Confidence Score: 4/5
Last reviewed commit: 91a553f |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 91a553f70a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
src/browser/pw-session.ts
Outdated
| lastErr = err; | ||
| // Don't retry rate-limit errors; retrying worsens the 429. | ||
| const errMsg = err instanceof Error ? err.message : String(err); | ||
| if (errMsg.includes("rate limit") || errMsg.includes("429")) { |
There was a problem hiding this comment.
Restrict retry break to true HTTP 429 rate-limit errors
The new retry guard treats any error message containing "429" as rate limiting, which can match unrelated transient connection failures that include a port or URL segment like :42900 (for example ECONNREFUSED 127.0.0.1:42900). In those cases connectBrowser now exits after the first failure instead of retrying up to 3 times, reducing robustness for valid CDP endpoints that happen to include 429 in their address.
Useful? React with 👍 / 👎.
src/browser/pw-session.ts
Outdated
| if (errMsg.includes("rate limit") || errMsg.includes("429")) { | ||
| break; | ||
| } |
There was a problem hiding this comment.
Overly broad "429" substring match risks false positives
errMsg.includes("429") can match error messages that contain 429 for unrelated reasons — e.g., a timeout value like "timed out after 4290ms", a URL path like /api/v429/..., or a port number in a connection string. A false match would silently suppress retries for a transient (non-rate-limit) failure.
The errMsg.includes("rate limit") check is already sufficient: every 429 thrown by fetchCdpChecked and fetchBrowserJson in this codebase now carries "rate limit" in the message. The "429" fallback is only needed for raw Playwright errors, but those are unlikely to surface a plain "429" token in their messages.
Consider removing the "429" check and relying solely on the "rate limit" phrase, or using a more specific pattern like errMsg.includes("HTTP 429"):
| if (errMsg.includes("rate limit") || errMsg.includes("429")) { | |
| break; | |
| } | |
| const errMsg = err instanceof Error ? err.message : String(err); | |
| if (errMsg.includes("rate limit")) { | |
| break; | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/browser/pw-session.ts
Line: 369-371
Comment:
**Overly broad `"429"` substring match risks false positives**
`errMsg.includes("429")` can match error messages that contain `429` for unrelated reasons — e.g., a timeout value like `"timed out after 4290ms"`, a URL path like `/api/v429/...`, or a port number in a connection string. A false match would silently suppress retries for a transient (non-rate-limit) failure.
The `errMsg.includes("rate limit")` check is already sufficient: every 429 thrown by `fetchCdpChecked` and `fetchBrowserJson` in this codebase now carries `"rate limit"` in the message. The `"429"` fallback is only needed for raw Playwright errors, but those are unlikely to surface a plain `"429"` token in their messages.
Consider removing the `"429"` check and relying solely on the `"rate limit"` phrase, or using a more specific pattern like `errMsg.includes("HTTP 429")`:
```suggestion
const errMsg = err instanceof Error ? err.message : String(err);
if (errMsg.includes("rate limit")) {
break;
}
```
How can I resolve this? If you propose a fix, please make it concise.|
Thanks for picking this up! The implementation looks solid. A few observations from my testing that led to filing the original issue:
Happy to test this on my setup (Docker/GCP, cron jobs using Stagehand + Browserbase) once it's ready for review. |
|
Thanks @wirelessjoe! Glad the error messages match what you needed. Your original issue report was really clear - made it easy to reproduce and fix. |
|
Addressed bot feedback in 3702810: P2 - "429" substring false positives: Removed P2 - Inconsistent rate-limit messages: Exported |
|
This new 429 path reflects upstream response text directly into the thrown error in src/browser/cdp.helpers.ts:176-180. That text is provider-controlled for remote CDP/browser endpoints, so it should not be embedded verbatim in the error surface. That message is later logged and returned to the tool caller unchanged in src/agents/pi-tool-definition-adapter.ts:179-188, which creates an avoidable log / agent-output injection surface. It also reads the full body before slicing, so I think this should use a stable message instead, or at most a bounded and sanitized snippet / request-id style metadata. |
|
Good catch, @altaywtf - you're right that reflecting upstream response text verbatim into the error surface is an unnecessary injection risk. Fixed in 490b675: removed the |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 490b67536a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@mvanhorn sir... your claude is not running format before pushing commits 😅 |
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟡 Undrained fetch Response body on HTTP 429 in fetchCdpChecked can exhaust connections (DoS)
Description
In Node.js (v22+),
This risk is amplified because Vulnerable code: if (!res.ok) {
if (res.status === 429) {
// Do not reflect upstream response text into the error surface (log/agent injection risk)
throw new Error(`${resolveBrowserRateLimitMessage(url)} Do NOT retry the browser tool.`);
}
throw new Error(`HTTP ${res.status}`);
}Note: this repo already introduced a best-effort body cancellation helper ( RecommendationBefore throwing, cancel (or drain) the response body so the underlying socket can be reused. Minimal safe fix (best-effort cancel) for the new 429 branch: if (!res.ok) {
if (res.status === 429) {
try {
await res.body?.cancel();
} catch {
// best-effort
}
throw new Error(`${resolveBrowserRateLimitMessage(url)} Do NOT retry the browser tool.`);
}
throw new Error(`HTTP ${res.status}`);
}Better: apply the same pattern for all non-OK responses (either Analyzed PR: #40491 at commit Last updated on: 2026-03-10T22:03:48Z |
f2497e2 to
d844788
Compare
Detect HTTP 429 responses from Browserbase at the fetch level and throw errors with actionable messages instead of generic 'HTTP 429'. Break out of the CDP retry loop on rate-limit errors to avoid amplifying the 429. Closes openclaw#40390 Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Remove errMsg.includes("429") fallback from retry-skip logic in
pw-session.ts; the "rate limit" check alone is sufficient and avoids
false positives from unrelated errors containing "429" (e.g. timeouts,
port numbers, API paths)
- Export BROWSER_RATE_LIMIT_MESSAGE from client-fetch.ts and use it in
cdp.helpers.ts instead of hand-rolled message for consistent wording
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Stop reflecting provider-controlled response body into thrown errors on HTTP 429. The upstream text was embedded in error messages that reach logs and agent tool output, creating an avoidable log/output injection surface for remote CDP/browser endpoints. The stable BROWSER_RATE_LIMIT_MESSAGE already provides all the actionable information the user needs. Co-Authored-By: Claude Opus 4.6 <[email protected]>
5de372f to
13839c2
Compare
|
Merged via squash.
Thanks @mvanhorn! |
* main: (42 commits) test: share runtime group policy fallback cases refactor: share windows command shim resolution refactor: share approval gateway client setup refactor: share telegram payload send flow refactor: share passive account lifecycle helpers refactor: share channel config schema fragments refactor: share channel config security scaffolding refactor: share onboarding secret prompt flows refactor: share scoped account config patching feat(discord): add autoArchiveDuration config option (openclaw#35065) fix(gateway): harden token fallback/reconnect behavior and docs (openclaw#42507) fix(acp): strip provider auth env for child ACP processes (openclaw#42250) fix(browser): surface 429 rate limit errors with actionable hints (openclaw#40491) fix(acp): scope cancellation and event routing by runId (openclaw#41331) docs: require codex review in contributing guide (openclaw#42503) Fix stale runtime model reuse on session reset (openclaw#41173) docs: document r: spam auto-close label fix(ci): auto-close and lock r: spam items fix(acp): implicit streamToParent for mode=run without thread (openclaw#42404) test: extract sendpayload outbound contract suite ...
…enclaw#40491) Merged via squash. Prepared head SHA: 13839c2 Co-authored-by: mvanhorn <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
…enclaw#40491) Merged via squash. Prepared head SHA: 13839c2 Co-authored-by: mvanhorn <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
…enclaw#40491) Merged via squash. Prepared head SHA: 13839c2 Co-authored-by: mvanhorn <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
…enclaw#40491) Merged via squash. Prepared head SHA: 13839c2 Co-authored-by: mvanhorn <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
…enclaw#40491) Merged via squash. Prepared head SHA: 13839c2 Co-authored-by: mvanhorn <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
…enclaw#40491) Merged via squash. Prepared head SHA: 13839c2 Co-authored-by: mvanhorn <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
…enclaw#40491) Merged via squash. Prepared head SHA: 13839c2 Co-authored-by: mvanhorn <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
…enclaw#40491) Merged via squash. Prepared head SHA: 13839c2 Co-authored-by: mvanhorn <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
…enclaw#40491) Merged via squash. Prepared head SHA: 13839c2 Co-authored-by: mvanhorn <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
…enclaw#40491) Merged via squash. Prepared head SHA: 13839c2 Co-authored-by: mvanhorn <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
Fixes #40390
Summary
When Browserbase returns 429 (max concurrent sessions exceeded), users see a generic "Browser control failed" message leading to unnecessary gateway restarts and agent retry loops.
This PR detects 429 responses and surfaces actionable error messages with explicit "do not retry" guidance.
Changes
src/browser/client-fetch.ts- Detect 429 in both HTTP and dispatcher paths, throwBrowserServiceErrorwith rate-limit message andBROWSER_TOOL_MODEL_HINTsrc/browser/cdp.helpers.ts- Detect 429 infetchCdpCheckedwith descriptive errorsrc/browser/pw-session.ts- Break out ofconnectBrowserretry loop on rate-limit errors (prevents retry amplification)src/browser/client-fetch.loopback-auth.test.ts- 4 test cases for 429 detectionError message
This contribution was developed with AI assistance (Claude Code).