Repository navigation
fix(browser-use): fail fast when no Native Messaging host can be registered - #13699
Conversation
âĶstered
createBrowserBackend() skips Native Messaging host registration outside
darwin/linux but still hands back a runtime whose transport keeps its own
35s connect budget. With POLL_INTERVAL_MS at 100, every wait polls for a
host that provably cannot exist: browsers.get('chrome') blocks ~35s, the
named profile path pays it twice, and setupBrowserRuntime() clears its
singleton on failure so each retry pays again.
Lift the registration condition into two named predicates and reuse them
for a connectTimeoutMs of UNREGISTERABLE_HOST_CONNECT_TIMEOUT_MS, so the
guard fires on exactly the setups registration skips and never on a
managed QWEN_BROWSER_USE_SOCKET_PATH / QWEN_BROWSER_USE_DISCOVERY_DIR
endpoint that can legitimately connect. profiles() still resolves [] and
start() still rejects BROWSER_DISCONNECTED with the message from #13675;
only the wait goes away.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-issue-patrol/jmuzsegeh53
|
â
Qwen Triage finished â CI landed green on â
Qwen Triage å·ēåŪæ ââ |
|
Thanks for the PR â easy to follow, and the reasoning in the description held up against the source. Template â every required section is filled in, including the Tested-on table and a real before/after. Problem â observed, not theoretical. #13692 is open, labelled Direction â aligned. The condition the guard uses is the one the docs already publish as policy ("Native Host installation currently supports macOS and Linux", Size â not applicable: no core-infrastructure path ( Approach â scope feels right, and I'd have landed in about the same place: the decision belongs in the backend factory, since that's the only spot that knows both that registration was skipped and whether a managed override publishes an endpoint. Pushing the platform check down into the transport would be worse â it has no business knowing the installer's policy, and One question, not a blocker. The guard is keyed on registration, not on what discovery actually finds, so the residual case is a live record in the default discovery dir on a non-registrable platform. Your Risk section says that can't exist because registration is gated on the same platform list â nearly true, but the host/discovery/protocol stack below the installer is win32-aware ( Risk â no elevated risk signals: neither file matches the revert-correlated paths. Moving on to code review. ð äļæčŊīææč°ĒčīĄįŪïžčŋäļŠ PR åūåĨ―čŊŧïžæčŋ°éįæĻįåæšį åŊđåūäļã æĻĄæŋ â åŋ åĄŦįŦ čé―åå Ļäšïžå æŽ Tested-on čĄĻæ žåįåŪį before/afterã éŪéĒââæŊå·ēč§æĩå°į bugïžäļæŊįčŪšæ§å åšã#13692 äŧäļš openïžåļĶ æđåââåŊđé―ãåŪåŦæįĻįæĄäŧķæĢæŊææĄĢå·ēįŧå
ŽåļįįįĨïž č§æĻĄââäļéįĻïžæēĄæč§Ķåæ ļåŋåšįĄčŪūæ―č·Ŋåūïž æđæĄââčåīåįïžæčŠå·ąäđäžčĩ°å°å·ŪäļåĪįä―į―ŪïžčŋäļŠåĪæå°ąčŊĨæūåĻ backend å·Ĩåéïžå äļšåŠæéĢéåæķįĨéãæģĻåčĒŦč·ģčŋãåãæŊåĶææįŪĄ override åĻååļ endpointããæåđģå°åĪæäļæĻå° transport æīå·ŪââåŪäļčŊĨįĨéåŪčĢ
åĻįåđģå°įįĨïžčäļ äļäļŠįéŪïžäļæŊéŧåĄéĄđãåŪåŦįäūæŪæŊãč―åĶæģĻåãïžčäļæŊ discovery åŪé
åį°äšäŧäđïžæäŧĨåĐäļįæ
å―ĒæŊïžåĻäļåŊæģĻåįåđģå°äļïžéŧčŪĪ discovery įŪå―éåšį°äšäļæĄåæīŧčŪ°å―ãRisk äļččŊīčŋį§æ
å―ĒäļåŊč―ååĻïžå äļšæģĻåååäļäŧ―åđģå°æļ
åéåķââåšæŽæįŦïžä―åŪčĢ
åĻäđäļį host/discovery/protocol æ æŊ win32-aware įïž éĢéĐââæ åįš§éĢéĐäŋĄå·ïžäļĪäļŠæäŧķé―äļåđé äļ revert įļå ģįéŦéĢéĐč·Ŋåūã čŋå ĨäŧĢį åŪĄæĨ ð â Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
Code reviewRead against What I checked instead of assuming:
Non-blocking, both already touched in Stage 1:
Test evidenceUnattended CI run â I did not build, run, or check out anything from this PR. What follows is the PR's own CI, fetched from the API for the reviewed commit, plus the static verification above. Not verified: any behaviour on a real Windows host. No lane witnesses it â Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / æŊäļŠæĢæĨåäļčĄïžåææ°äļæŽĄčŋčĄïžïžįįĨ skippedïžåĪąčīĨéĄđæåĻæåã 26 check-runs on this commit, 0 failures at the time of writing. The lane that matters â Sandboxed verification would settle the one claim CI cannot: äļæčŊīæäŧĢį åŪĄæĨåŊđį æåŪé åŧæ ļåŪïžčäļæŊį §åæĨåïžįįđïž
ééŧåĄéĄđïžäļĪįđåĻ Stage 1 é―æčŋïž
æĩčŊčŊæŪæ äššåžåŪį CI čŋčĄââææēĄææåŧšãčŋčĄæ checkout čŋäļŠ PR įäŧŧä―äļčĨŋãäļéĒæŊčŊĨ PR čŠå·ąį CIïžéåŊđčĒŦåŪĄæĨ commit éčŋ API ååūïžå äļäļčŋ°éææ ļåŪã æŠéŠčŊïž įåŪ Windows äļŧæšäļįäŧŧä―čĄäļšãæēĄæ lane č―č§čŊåŪââ CI čĄĻæ žč§äļæđïžįą finalize å·Ĩä―æĩåĻ CI č·åŪååå°æīæ°ïžãčŊĨ commit äļå
ą 26 äļŠ check-runïžæ°åæķ 0 åĪąčīĨãå
ģéŪį æäļäļŠ CI æ æģåĪåŪįčŊīæģïžåŊäŧĨįĻæēįŪąéŠčŊæĨæķåĢïž â Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
|
Confidence: 4/5 â clean at every stage; what keeps it from a 5 is two cosmetic nits and one residual case in an unsupported configuration, none of which change behaviour for anything the installer can produce. Before reading the diff I wrote down what I'd do from the issue alone: the decision belongs in the backend factory, because that's the only place that knows both that registration was skipped and whether a managed override publishes an endpoint; express it as a near-zero connect budget on the transport rather than an early throw; and key it on the same condition registration uses, not on the platform alone. That's what this PR does. The alternatives I weighed are worse and the description argues two of them better than I would have â throwing from the factory would strand #13675's reworded win32 message and change the observable failure mode, and teaching the transport the installer's platform policy would be the wrong layer ( It solves something users actually hit, not a hypothetical: 35s per wait, roughly 70s on the named-profile path, and again on every retry because the bundled skill clears its runtime singleton on failure. The linked issue is a triaged P2 with measurements, split out of the parent report deliberately. The code is straightforward and I'd thank rather than curse it in six months â one constant, two predicates, one ternary, 41 production lines. Every edit is in service of the stated goal: no drive-by refactor, no formatting churn, and the extraction is the minimum needed to keep the install gate and the timeout gate from drifting apart. The test additions are the part that impressed me most; I went in expecting pins that pass either way and found four cases that fail if the guard is broadened, because a wrongly-applied 0 budget turns Am I approving because I ran out of reasons to say no? The two I have left are the ones from Stage 2: the predicate wrappers are each called once, two lines apart, where inline consts would carry the same meaning with less indirection; and the comment above the constant says "provably nothing to wait for", which holds for every configuration our installer can produce but not for a hand-registered host publishing into the default discovery dir on such a platform. Neither is worth holding the PR for â the first is taste, the second is a comment clause in a configuration the design doc already declares unsupported and #13675 is about to report as such. One note for whoever merges, not a concern about the change: this author also has #13675 open in the same disconnect path. I verified the non-overlap claim from the diffs rather than taking it (zero shared files), and this PR deliberately routes through Approval is deferred, not withheld. äļæčŊīæConfidence: 4/5 ââ åéķæŪĩé―åđēåïžæēĄå° 5 åæŊå äļšäļĪäļŠįšŊč§æįå°éŪéĒïžäŧĨåäļäļŠåŠååĻäšãäļåæŊæé į―ŪãéįåĐä―æ å―Ēïžäšč é―äļäžæđååŪčĢ åĻč―äš§åšįäŧŧä―é į―ŪäļįčĄäļšã åĻčŊŧ diff äđåïžæå
æ issue åäļäščŠå·ąįåæģïžčŋäļŠåĪæåščŊĨæūåĻ backend å·Ĩåéïžå äļšåŠæéĢéåæķįĨéãæģĻåčĒŦč·ģčŋãåãæŊåĶææįŪĄ override åĻååļ endpointãïžįĻįŧ transport äž äļäļŠæĨčŋ 0 įčŋæĨéĒįŪæĨčĄĻčūūïžčäļæŊæåæéïžåđķäļåŪåŦæĄäŧķčĶäļæģĻåæįĻįæĄäŧķäļčīïžčäļæŊåŠįåđģå°ãčŋäļŠ PR åįæĢæŊčŋäŧķäšãææčĄĄčŋįå
ķäŧæđæĄé―æīå·Ūïžčäļå
ķäļäļĪäļŠæđæĄä―č
čŪščŊåūæŊæäžåįæīåĨ―ââäŧå·ĨåéæéäžčŪĐ #13675 éåį win32 ææĄåææŧäŧĢį ïžčŋäžæđååŊč§æĩįåĪąčīĨå―ĒæïžæåŪčĢ
åĻįåđģå°įįĨæįŧ transport åæŊæūéäšåąïž åŪč§ĢåģįæŊįĻæ·įäžįǰå°įéŪéĒïžäļæŊåæģïžæŊæŽĄįåū 35 į§ïžå ·å profile č·ŊåūįšĶ 70 į§ïžčäļå äļš bundled skill åĻåĪąčīĨæķäžæļ æ runtime åäūïžæŊæŽĄéčŊé―čĶåäŧäļéãå ģč issue æŊå·ēåčŊį P2ïžåļĶåŪæĩæ°æŪïžåđķäļæŊåŧæäŧįķæĨåéæåšæĨįã äŧĢį åūįīį―ïžå
äļŠæåæäžæč°ĒčäļæŊåéŠåŪââäļäļŠåļļéãäļĪäļŠč°čŊãäļäļŠäļå
čĄĻčūūåžïž41 čĄįäš§äŧĢį ãæŊåĪæđåĻé―æåĄäšæĒåŪįŪæ ïžæēĄæéĄšæéæïžæēĄææ žåžååŠéģïžčéĢæŽĄæ―åæĢæŊčŪĐãåŪčĢ
éĻãäļãčķ
æķéĻãäļäžå―žæĪæžį§ŧæéįæå°æđåĻãæĩčŊéĻåæčŪĐææåĪïžæåäŧĨäļšäžįå°éĢį§æđäļäļæđé―čŋįéåïžįŧææŊåäļŠäļæĶåŪåŦčĒŦæūåŪ―å°ąäžåĪąčīĨįįĻäūââå äļšéčŊŊå°åĨįĻ 0 éĒįŪäžčŪĐ ææŊäļæŊå äļščŊīäļåšååŊđįįąæ approveïžæåĐäļįäļĪæĄæĢæŊ Stage 2 ééĢäļĪæĄïžäļĪäļŠč°čŊåčŠåŠčĒŦč°įĻäļæŽĄãįļéäļĪčĄïžåæå čåļļéåŦäđįļåčéīæĨåąæīå°ïžåļļéäļæđįæģĻéåį "provably nothing to wait for"ïžčŋåŊđæäŧŽįåŪčĢ åĻč―äš§åšįææé į―Ūé―æįŦïžä―åŊđãåĻčŋįąŧåđģå°äļæåæģĻåãæčŪ°å―ååļå°éŧčŪĪ discovery įŪå―ãį Host äļæįŦãäļĪæĄé―äļåžåūåĄä―čŋäļŠ PRââįŽŽäļæĄæŊåĢåģéŪéĒïžįŽŽäšæĄæŊčŪūčŪĄææĄĢå·ēįŧå̰æäļæŊæã#13675 äđæĢčĶæĨæäļæŊæįé į―ŪéįäļåĨæģĻéæŠčūã įŧååđķč
įäļæĄæįĪšïžäļæđåĻæŽčšŦæ å
ģïžåäļä―ä―č
åĻåäļäļŠæčŋč·Ŋåūäļčŋæ #13675 åĪäš openãææŊäŧäļĪčūđ diff æ ļåŪäšãéķéå ãįčŊīæģïžčäļæŊį
§åæĨåïžįĄŪåŪæēĄæå
ąäšŦæäŧķïžïžåđķäļæŽ PR åŧæčĩ° approval æŊåŧķåïžäļæŊæįŧã æåčŋæŪĩæķ â Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
qwen-code-review-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship â CI landed green after the review. â
qwen-code-review-bot
left a comment
There was a problem hiding this comment.
Reviewed â no blockers. Suggestions are inline.
Test Plan (not a blocker): src/runtime.test.ts â no such file or directory; src/bridge/transport/chrome-extension-transport.test.ts â no such file or directory; src/runtime.ts â no such file or directory.
äļæčŊīæ
å·ēåŪĄæĨââæ éŧæéŪéĒã åŧščŪŪč§čĄå čŊčŪšã
Test Planïžééŧæïžïžsrc/runtime.test.ts â no such file or directory; src/bridge/transport/chrome-extension-transport.test.ts â no such file or directory; src/runtime.ts â no such file or directoryã
â qwen3.8-max via Qwen Code /review (v0.25.0)
Both design docs still promise that browser listing and selection may wait out the 35s discovery window. That no longer holds where no Native Messaging Host can be registered and no managed endpoint is configured, which is exactly the case this branch short-circuits (#13692); the docs are the normative spec for this subsystem, so leaving the claim unqualified invites the fast-fail to be "restored" as a regression. Also single-source the registration platform allowlist in the installer, which owns the fact, so the installer's guard and the runtime's fast-fail read one constant and cannot drift apart when win32 registration lands. The comment on that constant now records the real (implementation-shaped) reason, rather than the Chrome-limitation story the previous comment told. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmuzzjnorfk
qwen-code-review-bot
left a comment
There was a problem hiding this comment.
Partially reviewed â gaps disclosed. Suggestions are inline.
Not explored to full depth (tool budget reached): "agent 6a": none â no check was cut short..
Not reviewed: reverse audit â stopped before round 1 by the review time budget.
Test Plan (not a blocker): src/runtime.test.ts â no such file or directory; src/bridge/transport/chrome-extension-transport.test.ts â no such file or directory; src/runtime.ts â no such file or directory; Tests 14 passed â this review observed 591, 2633, 39964, 35646, 1028, 2172, 609, 11087 passed; 12 passed â this review observed 591, 2633, 39964, 35646, 1028, 2172, 609, 11087 passed; and 3 more.
äļæčŊīæ
äŧ åŪæéĻååŪĄæĨïžåŪĄæĨįžšåĢå·ēæŦéēã åŧščŪŪč§čĄå čŊčŪšã
æŠæĒįīĒå°å
ĻéĻæ·ąåšĶïžčūūå°å·Ĩå
·č°įĻéĒįŪïžïž"agent 6a"ïžnone â no check was cut short.ã
æŠåŪĄæĨïžåååŪĄčŪĄââčŊåŪĄæķéīéĒįŪäļčķģïžæŠč―åžå§įŽŽ 1 č―Ūã
Test Planïžééŧæïžïžsrc/runtime.test.ts â no such file or directory; src/bridge/transport/chrome-extension-transport.test.ts â no such file or directory; src/runtime.ts â no such file or directory; Tests 14 passed â this review observed 591, 2633, 39964, 35646, 1028, 2172, 609, 11087 passed; 12 passed â this review observed 591, 2633, 39964, 35646, 1028, 2172, 609, 11087 passed; and 3 moreã
â qwen3.8-max via Qwen Code /review (v0.25.0)
"Where neither applies" bound "neither" to the pair of override variables named in the sentence just before it, so it read as "with neither override configured, initialization attempts no registration" -- the inverse of `if (!managed && registrable)` in runtime.ts. Name both conditions so the antecedent is explicit and state the exact complement of the gate: registration is skipped when the platform cannot register a Native Messaging Host, or an override is configured. Land the zh-CN twin in the same change. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmv05z50rfy
qwen-code-review-bot
left a comment
There was a problem hiding this comment.
Reviewed â no blockers. Suggestions are inline.
Test Plan (not a blocker): src/runtime.test.ts â no such file or directory; src/bridge/transport/chrome-extension-transport.test.ts â no such file or directory; src/runtime.ts â no such file or directory.
Convergence: round 3 posted 1 inline comment(s), 1 of them reported for the first time; the previous round posted 1 (1 new). Findings keep coming back to the same files: docs/design/browser-use.md (findings in round 1; 1 more now). The rate of new findings is not falling. A cluster that keeps producing siblings usually means the fixes are treating instances of a shared root cause â triaging that cause before the next round, or splitting an independent cluster into its own pull request, tends to end the loop faster than fixing them one at a time. Batching the remaining fixes and verifying them before the next push, or dropping this PR's reviews to --severity-floor critical, keeps the loop from re-deriving the same set. No Critical finding is open on this round, so merging and moving the remaining Suggestion threads to a follow-up issue is available as an ending â a merged pull request cannot diverge further. (Observation only â nothing was withheld from this review because of this observation.)
äļæčŊīæ
å·ēåŪĄæĨââæ éŧæéŪéĒã åŧščŪŪč§čĄå čŊčŪšã
Test Planïžééŧæïžïžsrc/runtime.test.ts â no such file or directory; src/bridge/transport/chrome-extension-transport.test.ts â no such file or directory; src/runtime.ts â no such file or directoryã
æķææ
åĩïžįŽŽ 3 č―Ūååļäš 1 æĄčĄå
čŊčŪšïžå
ķäļ 1 æĄæŊéĶæŽĄæåšïžäļäļč―Ūååļäš 1 æĄïžå
ķäļ 1 æĄéĶæŽĄæåšïžãåį°ååĪåå°åäļæđæäŧķïždocs/design/browser-use.mdïžįŽŽ 1 č―Ūå·ēåščŋåį°ïžæŽč―Ūåæ 1 æĄïžãæ°åį°įäš§åšéåšĶæēĄæäļéãäļäļŠäļæåįå
åžåį°įį°ïžéåļļæåģįéæĄäŋŪåĪåŠåĻåĪįåäļæ đå įåŪäūââå
åŪä―åđķåĪįčŊĨæ đå ïžææįŽįŦįį°ææåįŽį PRïžéåļļæŊéæĄäŋŪåĪæīåŋŦįŧæåūŠįŊãæåĐä―äŋŪåĪææäļæđãéŠčŊååæĻéïžæå°æŽ PR įčŊåŪĄéå° --severity-floor criticalïžåŊäŧĨéŋå
åūŠįŊååĪæĻåŊžåäļįŧåį°ãæŽč―ŪæēĄææŠåģį Criticalïžå æĪ"åå
ĨåæåĐä― Suggestion įšŋįĻč―Žå°åįŧ issue"æŊäļäļŠåŊéįįŧææđåžââå·ēåå
Ĩį PR äļäžįŧ§įŧåæĢãïžäŧ
äļšč§åŊââæŽč―ŪčŊåŪĄæŠå æĪæĢįäŧŧä―å
åŪđãïž
â qwen3.8-max via Qwen Code /review (v0.25.0)
Both added English sentences stated the skip condition as a capability ("no
Native Messaging Host can be registered"), but the gate the runtime reads is a
platform allowlist: `registrable = NATIVE_HOST_SUPPORTED_PLATFORMS.includes(
process.platform)` (runtime.ts:38-40), the constant being `['darwin',
'linux']`. The zh-CN twin already says "čĨåđģå°æ æģæģĻå Native Messaging Host",
so the two normative versions of this design doc published different
constraints for one gate.
Add the platform qualifier to both English sentences, keeping each sentence's
own connective: the registration skip is `managed || !registrable`
(runtime.ts:46) while the shortened discovery window is `!managed &&
!registrable` (runtime.ts:85-88). Registration failures on darwin/linux keep
taking the throw at runtime.ts:68-78 and are deliberately not covered by
either sentence.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmv0cemawgb
qwen-code-review-bot
left a comment
There was a problem hiding this comment.
No issues found. LGTM! â
Test Plan (not a blocker): src/runtime.test.ts â no such file or directory; src/bridge/transport/chrome-extension-transport.test.ts â no such file or directory; src/runtime.ts â no such file or directory.
äļæčŊīæ
æŠåį°éŪéĒãLGTMïžâ
Test Planïžééŧæïžïžsrc/runtime.test.ts â no such file or directory; src/bridge/transport/chrome-extension-transport.test.ts â no such file or directory; src/runtime.ts â no such file or directoryã
â qwen3.8-max via Qwen Code /review (v0.25.0)
chiga0
left a comment
There was a problem hiding this comment.
Tier: Standard (platform-gated fast-fail, +170/-12, 5 files).
No blockers. LGTM â
What I checked
-
connectTimeoutMsis a real field onChromeExtensionTransportOptions(line 51 ofbridge/transport/chrome-extension-transport.ts). Default is 35,000 ms; passing 0 sets the connect deadline toDate.now() + 0, so the transport fails immediately when no host will register. The two deadline sites (lines 213, 278) use it directly â there is no secondary guard that would override 0. -
hasManagedEndpointOverride()correctly handles bothQWEN_BROWSER_USE_SOCKET_PATHandQWEN_BROWSER_USE_DISCOVERY_DIR, with.trim()guarding whitespace-only values. Either env var alone is sufficient to skip the fast-fail â correct, because either is sufficient for a managed endpoint. -
NATIVE_HOST_SUPPORTED_PLATFORMSexport creates a single source of truth used by the installer check,describeChromeProfiles, and the new runtime predicate â no silent divergence. -
Test coverage: 4 new tests exercise all four branches (fast-fail on win32 + no env, quick rejection on win32 + no env, managed override preserves wait, linux native path preserves wait). The "Before" output in the PR description shows
expected NaN to be âĪ 1000(never settled); the "After" output shows it settling within 1000 ms â conclusive evidence thatconnectTimeoutMs: 0is wired end-to-end.
Not reviewed: the existing behavior of describeChromeProfiles and resolveOptions on darwin/linux (unchanged, not in diff scope).
Reviewed with AI assistance.
qwen-code-dev-bot
left a comment
There was a problem hiding this comment.
Verified at head 748147b.
Gate 1. Head SHA fixed as above; every conclusion targets that SHA only.
Gate 2. All four review threads are resolved, and no historical Critical or necessary Suggestion remains outstanding.
Gate 3. Required CI is 29 of 29 complete and green, with nothing pending, cancelled, or failing. For transparency, the main ruleset for this repository configures no required_status_checks rule, so I judged the substantive lanes and excluded only review-pr, route, delay-automatic-review and fallback-comment, which are the reviewer bot and housekeeping routing.
Gate 4. No new Critical. The fix rests on one invariant and I checked it instead of assuming it: a connectTimeoutMs of 0 has to survive construction, and it does, because ChromeExtensionTransport assigns options.connectTimeoutMs ?? 35_000 with nullish coalescing at line 86, so 0 is preserved where a logical or would have silently restored the 35 second default and turned the fast fail into a no-op. Both deadline computations at 213 and 278 then read that field, so the poll exits at once rather than waiting for an endpoint that provably cannot appear, since registration is impossible on the platform and no managed endpoint override is set. NATIVE_HOST_SUPPORTED_PLATFORMS is now the single source of truth at all three sites, describeChromeProfiles, resolveOptions and the runtime registrability test, so the fast fail and the installer support list cannot drift, and the comment records why win32 is absent and what adding it would require. hasManagedEndpointOverride preserves the previous environment semantics exactly, and both the managed and the registrable cases pass undefined, leaving the existing 35 second behavior untouched. The only changed behavior is the intended one.
Gate 5. Head SHA, PR state, required checks, and unresolved thread count were re read atomically immediately before submitting this review.
qqqys
left a comment
There was a problem hiding this comment.
APPROVE at 748147b07274e94b22595fe3223227f20c82edd4. No historical blocking issue exists on this PR, and my own Critical-only scan of the full diff found nothing that would block merging.
Historical review
Nothing to re-verify. Across four reviewer rounds every filed finding was a Suggestion â no Critical, and no CHANGES_REQUESTED carrying a correctness objection. The one DISMISSED review on this PR is an approval that CI landed green after, not a withdrawn blocker. All review threads are resolved.
What I verified in the source at this head
The fix is small, but its correctness rests on a 0 timeout surviving three places that could each silently turn it back into the 35s default or skip discovery entirely. I checked all three:
0is not folded into the default.ChromeExtensionTransportreads the option withoptions.connectTimeoutMs ?? 35_000(bridge/transport/chrome-extension-transport.ts:86), not||, soUNREGISTERABLE_HOST_CONNECT_TIMEOUT_MS = 0is preserved rather than read as "unset".- The fast path still performs one honest discovery pass.
profiles()usesfor (;;)and callsdiscoverChromeProfiles()before theDate.now() >= deadlinecheck (:213-222), andconnectWithinDeadline()is ado { âĶ } while(:278-297). With a zero deadline each therefore runs exactly once instead of never, so an endpoint that is already published is still found and the incompatible-version path still throwsversionMismatch()whenprofiles.length > 0. Only the wait for a late-arriving endpoint is removed, which is what #13692 asks for. - No zero or negative budget is handed downstream.
connectHost(âĶ, Math.max(1, deadline - Date.now()), âĶ)clamps the remaining budget, so the expired deadline cannot produce a0ms connect call.
The guard's own preconditions hold. hasManagedEndpointOverride() and supportsNativeHostRegistration() reproduce the previous inline condition exactly â !SOCKET_PATH?.trim() && !DISCOVERY_DIR?.trim() && (darwin || linux) is now !managed && registrable â so which platforms install a Host is unchanged, and only the connect budget differs. Deriving the platform list from the exported NATIVE_HOST_SUPPORTED_PLATFORMS rather than restating 'darwin'/'linux' also removes the drift the two resolveOptions/describeChromeProfiles sites previously allowed; both now read the same constant (native-host-installer.ts:291, :371).
The new tests pin all four branches that matter: fast browsers.list â [], fast browsers.get â BROWSER_DISCONNECTED, a managed override on such a platform still waiting out the budget, and a registrable platform still waiting out the budget. Each asserts on elapsed time rather than only on the result, which is what makes the "at once" claim testable.
CI
15 checks pass at this head, 56 are skipped, and nothing fails or is pending. No failure is attributable to this PR.
|
Released in v0.25.1. |
What this PR does
createBrowserBackend()deliberately skips Native Messaging Host registration when the platform is notdarwin/linux, but the runtime it returned still carried the transport's own 35s connect budget. This PR lifts that registration condition into two named predicates (hasManagedEndpointOverride(),supportsNativeHostRegistration()) and reuses them so the transport is built withUNREGISTERABLE_HOST_CONNECT_TIMEOUT_MS(0) exactly when registration was impossible and nothing else publishes an endpoint. The call graph, the returned runtime and every error message are unchanged â only the wait disappears.Why it's needed
Fixes #13692. On such a platform every wait polls for a host that provably cannot exist:
browsers.get('chrome')blocks ~35s before the honest message appears, the named-profile path in the bundled skill pays it twice (~70s), andsetupBrowserRuntime()clears its singleton on failure so each agent retry pays it again. The reporter measuredLIST {"resolved":[],"elapsedMs":35001}andGET {"threw":true,"code":"BROWSER_DISCONNECTED","elapsedMs":35001}straight from the transport's?? 35_000default withPOLL_INTERVAL_MS = 100.The guard is conditioned on the same thing registration is, not on the platform alone, per the ruling in the resolved R1-3 thread on #13675.
!explicitSocketPathinside the transport is not an equivalent condition â it derives only fromoptions.socketPath ?? QWEN_BROWSER_USE_SOCKET_PATHand ignoresQWEN_BROWSER_USE_DISCOVERY_DIR, so a discovery-dir-only setup can legitimately connect and must keep its full budget. Both cases are pinned by tests here.Interaction with #13675: zero file overlap â #13675 touches 0 lines of
packages/browser-use/src/runtime.ts, and this PR touches none of the files #13675 edits. Failing fast by throwing fromcreateBrowserBackend()was rejected on purpose: #13675 is still open and reworded the disconnect error viadisconnectedMessage(platform), and an early throw would make that win32 branch dead in production. Passing a near-zero connect budget instead keepsconnectWithinDeadline()on the path that produces the reworded message, so the two PRs compose in either merge order.Reviewer Test Plan
How to verify
The defect only occurs when
process.platform â {darwin, linux}, so a real unmocked reproduction is impossible on a Linux or macOS host, and the Windows CI lane cannot substitute:ci.ymlgatestest_windowsonmerge_group/schedule/workflow_dispatch, not onpull_request. There is no on-Windows witness in this PR. The reproduction is a deterministic, platform-stubbed test-level one â the same form #13675 already runs green on Linux CI â using the file's existingvi.useFakeTimers(), withdiscoverChromeProfilesmocked to publish nothing so the wait can only end at its deadline. The poll loop, the deadline arithmetic and the platform guard stay the real ones, and the 35s figure comes from the transport's own constants, not from a hard-coded expectation.New cases (all appended after the existing
it.each, nothing re-ordered,beforeEach/afterEachuntouched):lists no browsers at once where no Native Host can be registeredâ win32 + both env vars empty:dispatch('browsers.list')settles within the window and still resolves[].rejects a start at once where no Native Host can be registeredâ same setup:dispatch('browsers.get')settles within the window and still rejectsBROWSER_DISCONNECTED.still waits out the connect budget for a managed QWEN_BROWSER_USE_SOCKET_PATH / _DISCOVERY_DIR on such a platformâ win32 with a managed override:installer.ensureuncalled and the wait is not short-circuited.still waits out the connect budget where a Native Host is registeredâ linux, both env vars empty: registration happens and the wait is unchanged.No test wall-clock-waits: everything runs under fake timers and the whole file finishes in ~26ms of test time, so it stays inside the 5s off-pool
testTimeout(packages/browser-useis inOFF_POOL_DEFAULT).Evidence (Before & After)
Before (source unpatched, new tests present) â the two fail-fast cases hang for the whole observation window:
After (same tests, patched
runtime.ts):The three "still waits out the budget" cases pass both before and after â that is the point: they pin that the guard does not fire for a managed endpoint or for a registrable platform.
Tested on
test_windowsdoes not run onpull_request. The win32 behaviour is covered by stubbingprocess.platform.Environment (optional)
N/A â unit tests only, run in a worktree off
origin/main29aef7de40b752bb0464919ab4af8f08a988dd31.Commands run, with results:
npx vitest run src/runtime.test.ts(packages/browser-use)npx vitest run src/bridge/transport/chrome-extension-transport.test.ts(packages/browser-use)npx vitest run(wholepackages/browser-use)npx tsc --noEmit(packages/browser-use, i.e. itstypecheckscript)npx eslint src/runtime.ts src/runtime.test.tsnpx prettier --writeon both changed filesunchangedRisk & Scope
profiles()still resolves[]andstart()still rejectsBROWSER_DISCONNECTEDwith the Web Store text, they just stop taking ~35s. A setup where something else registers a Native Messaging Host on a non-darwin/linux platform and publishes to the default discovery directory would lose its wait; that setup cannot exist today, because registration itself is gated on the same platform list.?? 35_000default, toPOLL_INTERVAL_MS, or tosetupBrowserRuntime()'s singleton reset inpackages/core's bundled skill. Onlypackages/browser-use/src/runtime.tsandpackages/browser-use/src/runtime.test.tsare touched (novitest.config.tschange).createBrowserBackend()keeps its signature and its failure modes.Linked Issues
Fixes #13692
Related, non-overlapping: #13675 (reworded disconnect message, still open), #13663 (parent report).
äļæčŊīæ
čŋäļŠ PR åäšäŧäđ
createBrowserBackend()åĻåđģå°äļæŊdarwin/linuxæķäžæ æč·ģčŋ Native Messaging Host æģĻåïžä―åŪčŋåį runtime äŧįķåļĶį transport čŠå·ąį 35 į§čŋæĨéĒįŪãæŽ PR æéĢæŪĩæģĻåæĄäŧķæ―æäļĪäļŠå ·åč°čŊïžhasManagedEndpointOverride()ãsupportsNativeHostRegistration()ïžåđķåĪįĻåŪäŧŽïžåŠæåĻãæģĻåäļåŊč―ãäļãæēĄæåŦįæĨæšååļ endpointãæķïžæįĻUNREGISTERABLE_HOST_CONNECT_TIMEOUT_MSïž0ïžæé transportãč°įĻåūãčŋåį runtimeãææéčŊŊææĄé―æēĄåïžæķåĪąįåŠæéĢæŪĩįåū ãäļšäŧäđéčĶ
äŋŪåĪ #13692ãåĻčŋįąŧåđģå°äļïžæŊæŽĄįåū é―åĻč―ŪčŊĒäļäļŠčŊæäļåŊč―ååĻį Hostïž
browsers.get('chrome')čĶåĄįšĶ 35 į§æįŧåščŊåŪįæĨéïžbundled skill įå ·å profile č·ŊåūčĶäŧäļĪæŽĄïžįšĶ 70 į§ïžïžčsetupBrowserRuntime()åĪąčīĨæķäžæļ æåäūïžæäŧĨ agent æŊæŽĄéčŊé―čĶåäŧäļæŽĄãæĨåč įīæĨæ transport į?? 35_000éŧčŪĪåžäļPOLL_INTERVAL_MS = 100æĩå°LIST {"resolved":[],"elapsedMs":35001}ãGET {"threw":true,"code":"BROWSER_DISCONNECTED","elapsedMs":35001}ãæį § #13675 äļå·ē resolve į R1-3 čŪĻčŪšäļįčĢåģïžčŋäļŠåŪåŦįæĄäŧķåŋ éĄŧäļæģĻåįæĄäŧķäļčīïžčäļæŊåŠįåđģå°ãtransport å éĻį
!explicitSocketPathåđķäļįäŧ·ââåŪåŠæĨčŠoptions.socketPath ?? QWEN_BROWSER_USE_SOCKET_PATHïžåŋ―įĨäšQWEN_BROWSER_USE_DISCOVERY_DIRïžå æĪåŠé äš discovery įŪå―įåšæŊæŊåŊäŧĨæĢåļļčŋæĨįïžåŋ éĄŧäŋįåŪæīéĒįŪãäļĪį§æ åĩčŋéé―ææĩčŊéä―ãäļ #13675 įå ģįģŧïž æäŧķéķéå ââ#13675 åŊđ
packages/browser-use/src/runtime.tsæđäš 0 čĄïžæŽ PR äđæēĄæįǰ #13675 æđįäŧŧä―æäŧķãæäŧŽåŧææēĄæéæĐãäŧcreateBrowserBackend()įīæĨæéãïž#13675 äŧæŠååđķïžåŪéčŋdisconnectedMessage(platform)éåäšæčŋææĄïžæåæéäžčŪĐéĢäļŠ win32 åæŊåĻįäš§äļåææŧäŧĢį ãæđäļšäž å ĨæĨčŋ 0 įčŋæĨéĒįŪïžconnectWithinDeadline()äŧįķčĩ°åĻäš§åšéĢæŪĩæ°ææĄįč·ŊåūäļïžäļĪäļŠ PR æ čŪšč°å åé―č―įŧåãčŊåŪĄæĩčŊčŪĄå
åĶä―éŠčŊ
įžšé·åŠåĻ
process.platform â {darwin, linux}æķåšį°ïžæäŧĨåĻ Linux / macOS äļŧæšäļæ æģåįåۿРmock įåĪį°ïžWindows CI äđéĄķäļäļïžci.ymlétest_windowsåŠåĻmerge_group/schedule/workflow_dispatchäļč·ïžäļåĻpull_requestäļč·ãæŽ PR æēĄæ Windows åŪæšč§čŊã åĪį°æŊįĄŪåŪæ§įãåđģå°ææĄĐįæĩčŊįš§åĪį°ââäļ #13675 å·ēåĻ Linux CI äļč·įŧŋįå―ĒåžäļčīââåĪįĻčŊĨæäŧķæĒæįvi.useFakeTimers()ïžåđķædiscoverChromeProfilesmock æäļååļäŧŧä― endpointïžäšæŊčŋæŪĩįåū åŠč―åĻčŠå·ąį deadline äļįŧæãč―ŪčŊĒåūŠįŊãdeadline čŪĄįŪäļåđģå°åŪåŦé―æŊįåŪäŧĢį ïž35 į§čŋäļŠæ°åæĨčŠ transport čŠå·ąįåļļéïžčäļæŊåæŧįææåžãæ°åĒįĻäūïžå ĻéĻčŋ―å åϿǿ
it.eachäđåïžæēĄæéæïžbeforeEach/afterEachæŠæđïžïžlists no browsers at once where no Native Host can be registeredââ win32 + äļĪäļŠįŊåĒåéé―äļšįĐšïždispatch('browsers.list')åĻįŠåĢå įŧæïžäļäŧįķ resolve[]ãrejects a start at once where no Native Host can be registeredââ åæ ·įčŪūį―Ūïždispatch('browsers.get')åĻįŠåĢå įŧæïžäļäŧįķäŧĨBROWSER_DISCONNECTEDrejectãstill waits out the connect budget for a managed QWEN_BROWSER_USE_SOCKET_PATH / _DISCOVERY_DIR on such a platformââ win32 äļååĻæįŪĄ overrideïžinstaller.ensureäļčĒŦč°įĻïžįåū äđæēĄæčĒŦįč·Ŋãstill waits out the connect budget where a Native Host is registeredââ linux + äļĪäļŠįŊåĒåéäļšįĐšïžæģĻåį §åļļåįïžįåū æķéŋäļåãæēĄæäŧŧä―æĩčŊæįåŪæķéįåū ïžå ĻéĻčĩ° fake timersïžæīäļŠæäŧķįæĩčŊčæķįšĶ 26msïžå æĪįĻģįĻģč―åĻįĶŧæą æķ 5 į§į
testTimeoutäđå ïžpackages/browser-useåĻOFF_POOL_DEFAULTéïžãååčŊæŪ
äŋŪåĪåïžæšį æŠæčĄĨäļãæ°æĩčŊå·ēå°ąä―ïžââäļĪäļŠ fail-fast įĻäūåĻæīäļŠč§æĩįŠåĢå é―æēĄæįŧæïž
äŋŪåĪåïžåæ ·įæĩčŊïž
runtime.tså·ēæčĄĨäļïžïžäļäļŠ "still waits out the budget" įĻäūåĻäŋŪåĪååé―éčŋââčŋæĢæŊéįđïžåŪäŧŽéä―ãæįŪĄ endpoint æåŊæģĻååđģå°äļåŪåŦäļč§Ķåãã
æĩčŊįŊåĒ
test_windowsäļåĻpull_requestäļčŋčĄãwin32 čĄäļšéčŋææĄĐprocess.platformčĶįãįŊåĒïžåŊéïž
N/A ââ åŠč·åå æĩčŊïžåĻåšäš
origin/main29aef7de40b752bb0464919ab4af8f08a988dd31į worktree äļæ§čĄãåŪé æ§čĄįå―äŧĪäļįŧæč§äļæđčąæčĄĻæ žïžéæĄäļčīïžã
éĢéĐäļčåī
profiles()äŧįķ resolve[]ïžstart()äŧįķäŧĨåļĶ Web Store ææĄįBROWSER_DISCONNECTEDrejectïžåŠæŊäļåčąįšĶ 35 į§ãåĶæååĻãįąåŦįįĻåšåĻé darwin/linux åđģå°äļæģĻå Native Messaging Host åđķååļå°éŧčŪĪ discovery įŪå―ãįåšæŊïžåŪäžåĪąåŧčŋæŪĩįåū ïžä―äŧåĪĐčŋį§åšæŊäļåŊč―ååĻïžå äļšæģĻåæŽčšŦå°ąååäļäŧ―åđģå°æļ åéåķã?? 35_000éŧčŪĪåžãPOLL_INTERVAL_MSïžäđæēĄææđpackages/corebundled skill ésetupBrowserRuntime()įåäūéį―ŪãåŠįǰäšpackages/browser-use/src/runtime.tsäļpackages/browser-use/src/runtime.test.tsïžæēĄææđvitest.config.tsïžãcreateBrowserBackend()įįūåäļåĪąčīĨæĻĄåžäŋæäļåãå ģč Issue
Fixes #13692
įļå ģä―éķéå ïž#13675ïžéåæčŋææĄïžäŧæŠååđķïžã#13663ïžįķæĨåïžã