Skip to content

fix(http): Gmail/Photos/Slides/Zoom hang when a peer never answers - #995

Open
SebTardif wants to merge 1 commit into
openclaw:mainfrom
SebTardif:fix/http-default-client-timeout
Open

fix(http): Gmail/Photos/Slides/Zoom hang when a peer never answers#995
SebTardif wants to merge 1 commit into
openclaw:mainfrom
SebTardif:fix/http-default-client-timeout

Conversation

@SebTardif

Copy link
Copy Markdown

What Problem This Solves

Fixes an issue where users running Gmail tracking queries, Photos media downloads, Slides thumbnail downloads, or Zoom API calls would hang forever when the remote peer accepted TCP but never sent response headers.

Why This Change Was Made

Those paths used http.DefaultClient (no timeout). Authenticated Google clients already bound ResponseHeaderTimeout at 30s without a full Client.Timeout, so large downloads are not cut short after headers arrive. This change reuses that transport for the leftover unauthenticated fetches, and gives Zoom a 30s client timeout for its small JSON API.

User Impact

Those commands now fail with a timeout error after about 30 seconds instead of hanging until the process is killed.

Evidence

terminal output from a live silent peer on 127.0.0.1:62317. curl shows the peer never answers. NewBoundedHTTPClient (the same client the patched call sites now use) returns after 30s with timeout awaiting response headers:

$ curl -sS --max-time 2 http://127.0.0.1:62317/
curl: (28) Operation timed out after 2004 milliseconds with 0 bytes received

$ go run ./cmd/timeoutproof http://127.0.0.1:62317/hang
client.Timeout=0s headerTimeout=30s
elapsed=30.002s err=Get "http://127.0.0.1:62317/hang": net/http: timeout awaiting response headers

Real behavior proof

  • Behavior or issue addressed: Unauthenticated HTTP fetches and Zoom's default client could wait forever on a silent peer.
  • Real environment tested: macOS 15, Go from this repo's go.mod, patched worktree at /tmp/gogcli-http-timeout, silent TCP listener on 127.0.0.1:62317.
  • Exact steps or command run after this patch: Started a listener that accepts connections and never writes headers. Ran curl --max-time 2 to confirm the peer is silent. Ran go run against googleapi.NewBoundedHTTPClient().Do for that URL.
  • Evidence after fix: terminal output above. The bounded client printed headerTimeout=30s and returned net/http: timeout awaiting response headers at 30.002s.
  • Observed result after fix: The hang is bounded. The same helper is now used by Gmail track opens, Photos downloads, Slides thumbnails, and nil Photos/Picker client fallbacks. Zoom NewClient uses a 30s Timeout instead of DefaultClient.
  • What was not tested: Live Google Photos, Slides CDN, tracking worker, or Zoom credentials.

Related

Unauthenticated Gmail tracking, Photos downloads, Slides thumbnails,
and Zoom API calls used http.DefaultClient and could hang forever on a
silent peer. Reuse the existing 30s ResponseHeaderTimeout transport for
those fetches, and give Zoom a 30s client timeout.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
@clawsweeper

clawsweeper Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

@clawsweeper clawsweeper Bot added merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Aug 14, 2026
@clawsweeper

clawsweeper Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed August 15, 2026, 6:43 AM ET / 10:43 UTC.

ClawSweeper review

What this changes

The PR replaces unbounded Gmail tracking, Photos, Slides, and Zoom HTTP clients with 30-second bounded clients and adds focused coverage.

Merge readiness

⚠️ Ready for maintainer review - 2 items remain

Keep open: current v0.37.0 still uses unbounded default HTTP clients on the affected paths, while this PR applies the repository’s established 30-second response-header policy and supplies a live silent-peer trace. No blocking patch defect was found; a maintainer should explicitly accept the bounded-availability tradeoff before merge.

Priority: P2
Reviewed head: 2e174e39179e44b21233455e7cfc93855141cd39
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused patch with strong source-level coverage and credible live terminal proof, pending only the maintainer’s timeout-policy acceptance.
Proof confidence 🐚 platinum hermit (4/6) Sufficient (terminal): The PR body contains a post-change terminal trace from a live silent peer showing the patched bounded client return after about 30 seconds.
Patch quality 🦞 diamond lobster (5/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The PR body contains a post-change terminal trace from a live silent peer showing the patched bounded client return after about 30 seconds.
Evidence reviewed 7 items Current-main gap: The released main revision still sends Gmail tracking requests through http.DefaultClient, so a peer that accepts TCP but never returns headers has no response-header bound there.
Established transport policy: Current main already uses a 30-second ResponseHeaderTimeout for authenticated Google clients while deliberately avoiding a full client timeout for large downloads.
PR implementation: The new bounded client clones the established base transport, preserving proxy and TLS defaults while applying the header timeout; direct Gmail, Photos, and Slides fetches use it.
Findings None None.
Security None None.

How this fits together

Several gog CLI commands make direct HTTP requests after obtaining API data or credentials. This change determines whether a nonresponsive remote peer yields a bounded error or leaves the command waiting indefinitely.

flowchart LR
A[CLI command] --> B[HTTP request]
B --> C{Service path}
C --> D[Google fetch transport]
C --> E[Zoom JSON client]
D --> F[Remote service]
E --> F
F --> G[Response or timeout error]
Loading

Decision needed

Question Recommendation
Should gog accept a 30-second failure for unusually slow Gmail tracking, Photos, Slides, and Zoom responses in exchange for preventing indefinite command hangs? Accept the bounded wait: Merge the consistent 30-second bounds, relying on the live silent-peer proof and the existing authenticated-client precedent.

Why: The implementation matches an established authenticated-Google policy, but it intentionally changes the failure mode for existing slow peers.

Before merge

  • Resolve merge risk (P2) - Merging intentionally turns requests that take more than 30 seconds to produce headers into errors; Zoom’s full client timeout also bounds its token and small JSON response bodies.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Change footprint 12 files; production +24/-7, tests +194 Most added code is focused regression coverage around a small HTTP-client policy change.
Bounded call sites 7 existing client/call-site substitutions plus 1 shared factory The change covers the identified direct fetches and nil-client fallbacks without introducing dependencies.

Merge-risk options

Maintainer options:

  1. Accept bounded availability (recommended)
    Merge with the documented understanding that a peer failing to produce headers within 30 seconds now returns an error instead of hanging.
  2. Pause for a different timeout policy
    Hold the PR if maintainers want a configurable or longer timeout rather than the existing transport default.

Technical review

Best possible solution:

Adopt the existing 30-second Google response-header policy for the remaining unauthenticated fetches and retain Zoom’s narrow 30-second JSON-client bound.

Do we have a high-confidence way to reproduce the issue?

Yes: a silent TCP listener that accepts a request without returning headers is a high-confidence path, and current main’s affected callers use http.DefaultClient while the PR routes them to a 30-second bounded client.

Is this the best way to solve the issue?

Yes: reusing the established Google transport avoids truncating media after headers arrive, while Zoom only handles token and small JSON operations with its full timeout.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 45b5d766e137.

Labels

Label justifications:

  • P2: Indefinite waits affect real command completion but have a bounded, service-specific blast radius.
  • merge-risk: 🚨 availability: The new 30-second limits intentionally replace indefinite waiting with timeout errors for slow or silent peers.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body contains a post-change terminal trace from a live silent peer showing the patched bounded client return after about 30 seconds.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body contains a post-change terminal trace from a live silent peer showing the patched bounded client return after about 30 seconds.

Evidence

What I checked:

  • Current-main gap: The released main revision still sends Gmail tracking requests through http.DefaultClient, so a peer that accepts TCP but never returns headers has no response-header bound there. (internal/cmd/gmail_track_opens.go:54, 45b5d766e137)
  • Established transport policy: Current main already uses a 30-second ResponseHeaderTimeout for authenticated Google clients while deliberately avoiding a full client timeout for large downloads. (internal/googleapi/client.go:20, 45b5d766e137)
  • PR implementation: The new bounded client clones the established base transport, preserving proxy and TLS defaults while applying the header timeout; direct Gmail, Photos, and Slides fetches use it. (internal/googleapi/client.go:324, 2e174e39179e)
  • Zoom scope: Zoom only uses its default client for token exchange and small JSON meeting operations, and the PR gives that client a 30-second full request timeout. (internal/zoom/client.go:79, 2e174e39179e)
  • Focused coverage: The branch tests the bounded transport and verifies Gmail tracking and Slides thumbnail callers select the replacement client; Photos nil-client fallbacks and Zoom’s default are also covered. (internal/cmd/http_client_test.go:45, 2e174e39179e)
  • Real behavior proof: The PR body records a post-change macOS run against a live silent TCP listener: the same bounded client returned timeout awaiting response headers after 30.002 seconds. (2e174e39179e)

Likely related people:

  • Peter Steinberger: Available current-main blame attributes the existing Google transport and Zoom client boundary lines to this author; deeper retained history is not available locally. (role: recent area contributor; confidence: low; commits: 45b5d766e137; files: internal/googleapi/client.go, internal/zoom/client.go)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Confirm that accepting a 30-second failure for exceptionally slow peers is the intended availability policy.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (2 earlier review cycles)
  • reviewed 2026-08-14T21:58:25.514Z sha 2e174e3 :: needs maintainer review before merge. :: none
  • reviewed 2026-08-15T08:59:26.960Z sha 2e174e3 :: needs maintainer review before merge. :: none

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant