PublicKeyCredential.getClientCapabilities() never settles on secure origins

Author: oneinganCreated Apr 15, 2026Updated Aug 27, 2026

Version info:

bash
         ______     ,,
    ,.-"`      | ,-` |
  .^           ||    |
 /    ,-*^|    ||    |
;    /    |    ||    ;-*```^*.
;   ;     |    |;,-*`         \
|   |     |  ,-*`    ,-"""\    \
|    \   ,-"`    ,-^`|     \    |
 \    `^^    ,-;|    |     ;    |
  *;     ,-*`  ||    |     /   ;;
    `^^`` |    ||    |   ,^    /
          |    ||    `^^`    ,^
          |  _,"|        _,-"
          -*`   ****"""``

qutebrowser v3.7.0
Git commit: 
Backend: QtWebEngine 6.10.2
  based on Chromium 134.0.6998.208
  with security patches up to 144.0.7559.96 (plus any distribution patches)
  (source: api)
Qt: 6.10.2

CPython: 3.13.12
PyQt: 6.9.0

Qt wrapper info:
  PyQt6: success
  PyQt5: not imported
  -> selected: PyQt6 (via autoselect)

colorama: 0.4.6
jinja2: 3.1.6
pygments: 2.19.2
yaml: 6.0.3
adblock: 0.6.0
objc: no
PyQt6.QtWebEngineCore: 6.10.0
PyQt6.sip: 6.11.1
pdf.js: 5.6.205 (/nix/store/ma9r0k6vyb1cw5bhln8y2qbj1hbfjm28-source/build/pdf.mjs)
sqlite: 3.51.2
QtNetwork SSL: OpenSSL 3.6.1 27 Jan 2026

Style: QFusionStyle
Qt Platform: wayland (/nix/store/zfjk8cllkx79ic8pnf1prwym5ik3fa1v-river-next-0.5.0-dev/bin/river -c /nix/store/3sma2x0xmw9kqr9mxhxf4a64nqs9a4l6-river-reka-init)
OpenGL: Intel, 4.6 (Compatibility Profile) Mesa 26.0.4
Platform: Linux-6.19.11-x86_64-with-glibc2.42, 64bit
Linux distribution: NixOS 26.05 (Yarara) (nixos)
Frozen: False
Imported from /nix/store/3d9gbgw3dn7sybx9vaz49cl5qp18vdp1-qutebrowser-3.7.0/lib/python3.13/site-packages/qutebrowser
Using Python from /nix/store/19lsw6qfzrsh7jbspwi0kibs7l860ywd-python3-3.13.12-env/bin/python3.13
Qt library executable path: /nix/store/9lf2mfhsk7s379mfsh8nqc4xabdg6dw6-qtbase-6.10.2/libexec, data path: /nix/store/9lf2mfhsk7s379mfsh8nqc4xabdg6dw6-qtbase-6.10.2

Does the bug happen if you start with --temp-basedir?:

Yes.

I reproduced this with fresh temporary basedirs (--temp-basedir / -B $(mktemp -d)) and no existing qutebrowser profile state.

Description

PublicKeyCredential.getClientCapabilities() exists in qutebrowser/QtWebEngine, but on secure origins it never resolves or rejects for me.

That seems to break sites which await this promise before continuing their auth flow. The concrete user-facing symptom I ran into was the ChatGPT homepage login modal getting stuck after entering the email address: the modal submits, disables its buttons, and never redirects to the password step. A direct OpenAI auth URL works, so the breakage appears to be in the capability-detection step used by the homepage modal.

After tracing that site with CDP, I found the relevant code path is waiting on PublicKeyCredential.getClientCapabilities(). I then reduced it to a smaller qutebrowser repro on https://example.com/.

On a secure origin, this expression times out after 3 seconds instead of settling:

javascript
await Promise.race([
  PublicKeyCredential.getClientCapabilities()
    .then(v => ({ ok: true, v }))
    .catch(e => ({ ok: false, err: String(e) })),
  new Promise(r => setTimeout(() => r({ timeout: true }), 3000)),
])

Actual result in qutebrowser for me:

javascript
{ timeout: true }

Expected result:

  • the promise should resolve with a capabilities object, or
  • reject with an error,
  • but it should not stay pending forever.

More evidence that this is the blocker: if I inject a document-start shim which replaces PublicKeyCredential.getClientCapabilities with an async function that immediately resolves to an all-false capabilities object, the exact same ChatGPT homepage login flow proceeds normally to the password page.

So this looks like a qutebrowser/QtWebEngine WebAuthn capability-detection hang rather than a site-specific login issue.

How to reproduce

Minimal API repro:

  1. Start qutebrowser with a fresh profile on any secure origin, for example:
    bash
    qutebrowser --temp-basedir https://example.com/
  2. Open a JS console for that page (I used QtWebEngine remote debugging / CDP).
  3. Evaluate:
    javascript
    await Promise.race([
      PublicKeyCredential.getClientCapabilities()
        .then(v => ({ ok: true, v }))
        .catch(e => ({ ok: false, err: String(e) })),
      new Promise(r => setTimeout(() => r({ timeout: true }), 3000)),
    ])
  4. Observe it timing out instead of settling.

Higher-level site symptom:

  1. Open https://chatgpt.com/ in a fresh qutebrowser temp-basedir.
  2. Click the homepage Log in button.
  3. Enter an email address and submit.
  4. Observe that the modal stays on chatgpt.com, disables its buttons, and never reaches the password page.
  5. As a control, opening the direct auth URL (https://auth.openai.com/log-in-or-create-account or https://chatgpt.com/auth/login) does continue to the password page.

I searched qutebrowser issues for getClientCapabilities, PublicKeyCredential, conditionalGet, passkey, and related WebAuthn terms, but didn't find this exact hang already reported. This may ultimately belong in QtWebEngine, but I'm filing it here first since qutebrowser users hit the broken API directly.