Skip to content

Let the bridge client outlast the bridge's own request timeout - #245

Open
philipp-winterle wants to merge 1 commit into
httptoolkit:mainfrom
philipp-winterle:fix-bridge-client-execute-timeout
Open

philipp-winterle wants to merge 1 commit into
httptoolkit:mainfrom
philipp-winterle:fix-bridge-client-execute-timeout

Conversation

@philipp-winterle

@philipp-winterle philipp-winterle commented Sep 17, 2026

Copy link
Copy Markdown

The problem

Two constants in this repo contradict each other on the same call path.

src/api/ui-operation-bridge.ts gives the UI 30 seconds to answer an operation:

const REQUEST_TIMEOUT_MS = 30_000;

src/api/bridge-client.ts gave up on that same request after 2 seconds:

timeout: 2000

So the bridge's budget was unreachable. Any operation that took longer than two
seconds - events.get-response-body on a large body, or a UI that was briefly
busy - failed on the client with a bare ETIMEDOUT while the bridge was still
waiting perfectly happily, and the bridge's own error message (which names the
operation) could never reach the caller.

Reproduced by holding a /api/execute response back for three seconds:

Error: Request timed out

The change

The timeout now depends on what the request is waiting for.
/api/execute waits just past REQUEST_TIMEOUT_MS; everything else keeps the
old 2 seconds, which matters because apiRequest walks a list of candidate
socket paths and a dead one has to fail fast to leave time for the paths behind
it. REQUEST_TIMEOUT_MS is exported so the two numbers can't drift apart again.

socketRequest is exported as well, purely so the new test can drive one known
socket. Going through apiRequest would walk its candidate paths, and the last
of those on macOS is the real HTTP Toolkit on the developer's own machine - the
first draft of this test silently executed its operation against my running
instance.

Out of scope, but worth flagging

apiRequest retries the next socket path when one fails, including on timeout.
With the longer execute timeout, a genuinely unresponsive UI on macOS now costs
~35s on the primary socket and then another ~35s on the Darwin fallback before
the caller sees anything. That shape predates this change, but the numbers get
bigger. Happy to follow up with a "don't retry other paths after a timeout"
change if you want it.

Test plan

  • New test/integration/bridge-client.spec.ts: an operation the UI answers
    after 3 seconds now succeeds (fails with Request timed out before this
    change), plus two guards that the execute timeout stays above
    REQUEST_TIMEOUT_MS and the discovery timeout stays well below it.
  • test:unit: 23 passing.
  • test:integration: 65 passing, 1 pending, 16 failing. main here fails 16
    too, and 62 pass, so the three added tests are the whole difference. The 16 on
    main are all environment-dependent - Docker CLI interception, Electron,
    Chrome, fresh and existing terminal, and the end-to-end server API test -
    none of which this machine can run. I captured that list on main only and
    compared failure counts rather than names, because rerunning the suite here
    launches real browsers and terminal windows.
  • tsc --noEmit clean.

The UI operation bridge gives each operation REQUEST_TIMEOUT_MS (30s) to
complete, but every request from the client side used a fixed 2s socket
timeout. Any operation that took longer than two seconds - a large
events.get-response-body, or a UI that was briefly busy - failed with a
bare ETIMEDOUT while the bridge was still waiting happily, so the
server's 30s budget was unreachable and its error never arrived.

The timeout now depends on what the request is waiting for: discovery
requests keep failing fast at 2s, so a dead socket path doesn't hold up
the paths behind it, while /api/execute waits just past the bridge's own
limit, which lets the bridge's error - naming the operation - through.

socketRequest is exported so the test can drive one known socket. Going
through apiRequest would walk its candidate paths, and on macOS the last
of those is the real server on the developer's machine.
@CLAassistant

CLAassistant commented Sep 17, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

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.

2 participants