Skip to content

bug: Personal Chrome connects and shares, but encoder never emits a keyframe — connect_personal times out after 90s with an empty error #301

Description

@PiefkePaul

Summary

Connecting "Browser Tools" → "Personal Chrome" (real, already-running Chrome via chrome://inspect/#remote-debugging, not the managed/headless browser) completes every step up through the OS-level window-share permission — Chrome shows the capture-sharing indicator bar — but the encoder in browser-encoder.html never produces a first video keyframe. The request blocks for exactly 90 seconds and then fails with HTTP 409 {"detail":""}, and the frontend shows an indefinite loading spinner with no visible error at all.

Environment

  • cptr version: 0.9.21
  • OS: Windows 11
  • Chrome: reproduced on both 153.0.8010.50 and 153.0.8010.53 (updated mid-investigation — no change in behavior)
  • browser.provider: local, browser.tab_default_mode: chrome, browser.tab_chrome_source: personal

Steps to reproduce

  1. In Chrome, open chrome://inspect/#remote-debugging and enable remote debugging on the already-running (personal) Chrome instance. Note the live port it shows (e.g. 127.0.0.1:63918).
  2. In cptr Settings → Websuche → Browsersteuerung → Browser-Tools für Agenten: enable, provider "Lokales CDP", or independently under "Browser-Tabs" → "Standard für Browser-Tabs": Chrome, "Chrome-Profil": Persönliches Chrome, CDP-URL = the port from step 1.
  3. Confirm the raw connection works: POST /api/browser/cdp {"url": "http://127.0.0.1:<port>"} → 200 {"browser":"Chrome/..."}. This always succeeds.
  4. Click "Verbinden" (Settings) or open a new "Browser" tab (+ → Browser), which calls POST /api/browser/personal.
  5. In Chrome, the "Choose what to share" dialog appears (window-list picker). Select the window titled "Open WebUI Computer Browser". Approve.
  6. Chrome shows the "is sharing a window" indicator bar — capture is genuinely active on the Chrome side.
  7. Wait. After 90 seconds: POST /api/browser/personal → 409 {"detail":""}. The tab UI never updates past "Browser wird gestartet…" (no error is ever surfaced to the user in the UI itself — only visible via the browser console: Failed to create browser session: o: Conflict).

Server-side evidence

09:52:16.799 INFO  WebSocket /api/browser/sessions/personal/encoder?token=... [accepted]
09:52:16.800 INFO  connection open
09:53:46.794 INFO  POST /api/browser/personal HTTP/1.1 409

Exactly 90.0s between encoder accept and the 409 — matches asyncio.wait_for(personal.first_keyframe.wait(), 90) in viewer.py's connect_personal. asyncio.TimeoutError stringifies to "", which is why the raised HTTPException(status_code=409, detail=str(exc)) in routers/browser.py carries no useful message — reproducible root cause of the empty {"detail":""}, and worth fixing as a UX issue on its own (the frontend should at least show something instead of an infinite spinner on any 4xx/5xx from this endpoint).

The encoder WebSocket connects and stays open the whole 90s (confirmed both via server log and via the Chrome wird von automatisierter Testsoftware gesteuert banner + active share indicator visible in the shared window) — so the failure is specifically that no type: "config"/frame data is ever received from the browser-encoder.html page's own capture pipeline (window.startCapture()), not a connection or permission problem.

What I tried, no effect

  • Setting browser.encoder.hardware_acceleration to prefer-software instead of no-preference — same 90s timeout.
  • Updating Chrome (153.0.8010.50 → .53) mid-investigation — identical behavior before and after.
  • Restarting cptr fully between attempts (rules out stale in-process state as the sole cause — first attempt after a clean restart also times out the same way).

Separate but related: shared cdp_url field

browser.cdp_url is one config key used by both "Browser-Tools für Agenten" (agent tool — needs headless Chrome, no real window) and "Browser-Tabs → Chrome → Personal" (needs a real, capturable window). Pointing it at a headless agent Chrome breaks Personal mode outright (no window to capture), and pointing it at the personal Chrome breaks the agent tool. There's no way to have both configured at once in the current UI/schema. Might be worth a second, independent config key for the two use cases, since they're fundamentally different capture mechanisms.

Suggested next step

Since the capture pipeline gets all the way to an active, user-approved share with a live encoder WebSocket but never emits a keyframe, I'd guess the issue is in the browser-encoder.html/browser-runtime.js WebCodecs setup (or the getDisplayMedia/chromeMediaSource stream handoff) rather than anything CDP- or permission-related. Happy to provide more logs/traces if useful — I have a full reproducible timeline including screenshots of the Chrome share picker and the (blank) encoder page mid-capture.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions