claude-in-chrome: an UNGRANTED host makes executeScript-based tools hang their full timeout and blame the page, instead of denying

Status Open
Reported on v2.1.267
Maintainer reply None cached
Activity 0 comments · opened Sep 10, 2026

Summary

An executeScript-based browser tool called on a host the extension has not been granted does not return a permission error. It hangs for its full timeout and reports that the page is busy or still loading:

Failed to extract page text: Page still loading
  (executeScript waited 45000ms for document_idle).
Error capturing screenshot: Script injection timed out after 5000ms —
  the page is busy or mid-navigation ...

Every clause of that is false. The page is readyState: "complete", the tab is visibilityState: "visible", the connection is healthy, and javascript_tool answers instantly on the same tab in the same browser_batch.

The message misdirects to three things that are all fine — the page, the connection, and the browser selection — and never mentions permissions. Cost here: two separate multi-hour investigations across two days, both of which concluded "the Chrome connection is unstable."

Reproduction

Grant the extension a host (e.g. www.google.com) and leave another ungranted (e.g. example.com). Then, in one tab:

| host | granted | javascript_tool | get_page_text | computer screenshot |
|---|---|---|---|---|
| www.google.com | yes | works | works | works |
| example.com | no | works | 45 000 ms timeout | 5 000 ms timeout |

Steps:

  1. tabs_context_mcp { createIfEmpty: true }
  2. navigate to the ungranted host.
  3. javascript_tool → succeeds, returns the real document.title, readyState: "complete", visibilityState: "visible".
  4. get_page_text on the same tab → hangs 45 s, then reports the page is still loading.
  5. navigate the same tab to the granted host → get_page_text and screenshot both succeed immediately.
  6. Control: navigate the same tab back to the ungranted host → fails again immediately.

Step 6 is what makes this a permission fault rather than a transient: the only variable is the host.

What appears to be happening

There seem to be (at least) three injection paths that fail differently on an ungranted host:

| path | tools | on an ungranted host |
|---|---|---|
| immediate evaluation | javascript_tool, navigate, chrome.tabs reads | succeeds |
| executeScript at document_idle | get_page_text, read_page, find, computer screenshot | hangs to timeout |
| up-front permission check | computer click | denies immediately, by name |

A computer click on an ungranted domain returns Permission denied for this action on this domain — immediate and explicit. So the extension can produce a correct denial; the executeScript path just doesn't consult the grant before waiting.

A related contrast, same setup: javascript_tool is denied immediately and by name on some granted hosts (Permission denied for JavaScript execution on this domain). So javascript_tool and executeScript consult different policies, and only one of them reports failure honestly.

Why this is worth fixing beyond the message

Chrome resets runtime_granted_permissions whenever its HMAC over extensions.settings fails to verify — a hard kill of Chrome can cause it. So grants can disappear with nobody having touched them, and the symptom is not "permission denied", it is every page-read and screenshot timing out while navigation keeps working. That reads exactly like a flaky connection.

Inside browser_batch it is worse: a 45 s hang blows the tool's response budget, so the batch returns browser_batch did not respond in time and the remaining actions are discarded — a third distinct message for the same root cause.

Expected behaviour

get_page_text, read_page, find and computer screenshot should check the host grant before waiting for document_idle, and return the same immediate, named error that computer click already returns — ideally naming the host and pointing at the extension's site-access UI.

Failing that, the timeout message should not assert that the page is loading when the extension has not established that it can inject at all.

Environment

  • macOS 26.6.2 (Apple Silicon)
  • Google Chrome 152.0.7977.83
  • Claude extension 1.0.91
  • Claude Code 2.1.267
  • One Chrome install, one profile, extension present in that profile only

View original on GitHub ↗