Skip to content

[fix]: close parity gaps across SDKs for local browser launching - #2864

Open
seanmcguire12 wants to merge 8 commits into
mainfrom
local-launcher-parity
Open

[fix]: close parity gaps across SDKs for local browser launching#2864
seanmcguire12 wants to merge 8 commits into
mainfrom
local-launcher-parity

Conversation

@seanmcguire12

@seanmcguire12 seanmcguire12 commented Aug 31, 2026

Copy link
Copy Markdown
Member

why

this PR addresses parity gaps in local launch behaviour:

  • the TypeScript and Go SDKs already waited for a newly launched Chrome process to expose a usable CDP endpoint, detected early process exit, & cleaned up owned resources when launch failed.
  • the Python SDK returned immediately after spawning Chrome & had narrower executable discovery, profile setup, cancellation, & process-tree shutdown behavior

Go & Python also accepted an explicit debugging port without first proving it was available. that could let launch continue toward a browser endpoint the SDK did not own

what changed

  • added a default Chrome flag fixture & made all three SDK test suites compare their local flag lists against it
  • added cross SDK contract coverage for launch argument ordering, about:blank, viewport behavior, & profile ownership
  • refactored the Python local launcher into focused validation, executable discovery, profile, readiness, & shutdown helpers
  • aligned Python Chrome discovery with TypeScript & Go:
    • explicit executable then CHROME_PATH then platform candidates
    • Chrome Canary, Stable, Beta, & Chromium on macOS
    • Canary & Stable across the standard Windows roots
    • the shared Chrome & Chromium executable order on Linux
  • validate Python launch options & explicit executables before creating a profile or spawning Chrome
  • create caller-provided Python profile directories recursively while preserving caller ownership
  • wait for a successful /json/version response with a non-empty webSocketDebuggerUrl before a Python launch resolves
  • race Python readiness against browser exit & initialization cancellation, then terminate the owned process & remove its temporary profile when launch does not complete
  • added explicit loopback port availability checks to the Python & Go launchers before profile creation or process spawn
  • return a focused occupied-port error instead of adopting or continuing toward an existing browser, while preserving other socket failures
  • aligned Python shutdown with the TypeScript & Go launchers:
    • automatically disable the Chromium sandbox for Linux root
    • terminate Unix process groups with graceful then forced signals
    • terminate Windows process trees with taskkill /T then /F
    • share one close task across concurrent callers
    • continue profile cleanup after shutdown failures & preserve combined errors

test plan

  • Verify all three SDKs use the same static default Chrome flags, argument ordering, starting URL, viewport behavior, & profile ownership contract.
  • Verify Python option validation, explicit executable precedence, CHROME_PATH, complete platform discovery order, unsupported platforms, & caller profile creation.
  • Verify Python waits for a valid CDP version response, rejects malformed readiness responses, detects early browser exit, & cleans up after spawn failure or cancellation.
  • Verify Python & Go automatic port selection, occupied explicit-port rejection before resource creation, & preservation of non-occupancy socket errors.
  • Verify Python Linux-root sandbox behavior, Unix graceful & forced process-group termination, Windows tree termination, already-exited processes, concurrent close, & combined cleanup errors

Summary by cubic

Closes local browser launching parity gaps across the Python, Go, and TypeScript SDKs. The Python launcher now waits for a ready CDP endpoint, detects early browser exit, and cleans up after failed or cancelled launches. Python and Go reject explicit debugging ports that are already occupied instead of proceeding toward a browser the SDK does not own. All three SDKs now treat an empty user data directory string as omitted, so the SDK creates and removes a temporary profile.

Bug Fixes

  • Python validates launch options, resolves the executable, and checks port availability before creating a profile or spawning Chrome.
  • Python waits for a non-empty webSocketDebuggerUrl from /json/version, racing that check against browser exit and cancellation.
  • Python caller-provided profile directories are created recursively while preserving caller ownership.
  • Python shutdown now disables the Chromium sandbox for Linux root, terminates Unix process groups and Windows trees with graceful-then-forced signals, shares one close task across concurrent callers, and reports combined cleanup errors.

Refactors

  • Python Chrome discovery matches TypeScript and Go: explicit path, then CHROME_PATH, then the same platform candidate order for macOS, Windows, and Linux.
  • Go's port resolution checks loopback availability before profile creation and maps occupied-port errors to a focused message.
  • All three SDK test suites compare their default Chrome flags against a shared fixture and cover cross-SDK contract tests for argument ordering, startup URL, viewport, and profile ownership.

Written for commit 5fc6d57. Summary will update on new commits.

Review in cubic

@changeset-bot

changeset-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5fc6d57

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@browserbasehq/stagehand-python Patch
@browserbasehq/stagehand-go Patch

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

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 7 files

Architecture diagram
sequenceDiagram
    participant App as SDK Consumer
    participant SDK as Python / Go / TypeScript SDK
    participant Launch as Local Chrome Launcher
    participant Env as Environment and Platform
    participant Port as Loopback Port
    participant Profile as Chrome Profile
    participant Chrome as Chrome Process
    participant CDP as Chrome CDP Endpoint

    Note over App,CDP: Local browser launch runtime flow shared across SDKs

    App->>SDK: Launch local browser(options)
    SDK->>Launch: Validate viewport, scale factor, proxy, and launch options
    alt Invalid options
        Launch-->>SDK: Validation error
        SDK-->>App: Reject launch before resource creation
    else Valid options
        Launch->>Env: Resolve executable
        alt Explicit executable is configured
            Env-->>Launch: Use explicit executable
        else CHROME_PATH is configured
            Env-->>Launch: Use CHROME_PATH executable
        else No explicit path
            Env-->>Launch: Search platform candidates in shared order
        end

        Launch->>Port: Inspect requested loopback port
        alt Automatic port
            Port-->>Launch: Available ephemeral port
        else Explicit port available
            Port-->>Launch: Confirm requested port
        else Explicit port occupied
            Port-->>Launch: Address-in-use error
            Launch-->>SDK: Focused occupied-port error
            SDK-->>App: Reject before profile creation or process spawn
        end

        Launch->>Profile: Resolve profile ownership
        alt Caller-provided profile
            Profile->>Profile: Create directory recursively
            Profile-->>Launch: Preserve caller-owned profile
        else SDK-created temporary profile
            Profile-->>Launch: SDK-owned profile marked for cleanup
        end

        Launch->>Launch: Build ordered shared Chrome flags
        Note over Launch,Chrome: Includes viewport, remote debugging port, profile path, and about:blank
        opt Linux process runs as root
            Launch->>Launch: Disable Chromium sandbox
        end

        Launch->>Chrome: Spawn executable with flags
        alt Spawn fails
            Chrome-->>Launch: Process creation error
            Launch->>Profile: Remove SDK-owned profile
            Launch-->>SDK: Combined launch and cleanup error if cleanup fails
            SDK-->>App: Reject launch
        else Spawn succeeds
            loop Until CDP is ready
                Launch->>CDP: GET /json/version
                alt Valid response with non-empty webSocketDebuggerUrl
                    CDP-->>Launch: CDP readiness
                else Not ready or malformed response
                    CDP-->>Launch: Retry after polling interval
                end
                Chrome-->>Launch: Process exit status
            end

            alt Chrome exits or initialization is cancelled before readiness
                Launch->>Chrome: Terminate owned process
                Launch->>Profile: Remove SDK-owned profile
                Launch-->>SDK: Launch failure and cleanup result
                SDK-->>App: Reject launch
            else Ready
                Launch-->>SDK: Resolved browser with CDP URL and close callback
                SDK-->>App: Return local browser
            end
        end
    end

    App->>SDK: Close local browser
    SDK->>Launch: Run shared close task
    alt Unix platform
        Launch->>Chrome: Graceful process-group termination
        opt Process group does not exit in time
            Launch->>Chrome: Forced process-group termination
        end
    else Windows platform
        Launch->>Chrome: taskkill /T
        opt Process tree remains
            Launch->>Chrome: taskkill /T /F
        end
    end
    Launch->>Profile: Remove SDK-owned profile
    Profile-->>Launch: Cleanup result
    Launch-->>SDK: Preserve caller profile and report combined errors
    SDK-->>App: Close complete
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/sdk-python/src/stagehand/browser.py Outdated
Comment thread packages/sdk-python/tests/test_browser.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant