Skip to content

fix(capture): retry content-script message on one-click auto-capture#11

Merged
DCCA merged 1 commit into
mainfrom
fix/capture-content-script-race
Jun 27, 2026
Merged

fix(capture): retry content-script message on one-click auto-capture#11
DCCA merged 1 commit into
mainfrom
fix/capture-content-script-race

Conversation

@DCCA

@DCCA DCCA commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Symptom

After enabling one-click capture (#8), clicking the toolbar icon opened the editor but capture failed with:

Could not establish connection. Receiving end does not exist.

The manual Capture Page button still worked.

Root cause

One-click auto-capture fires immediately on editor load. captureFullPage sends SB_GET_PAGE_METRICS to the target tab's content script — but that listener hasn't registered yet that early, so chrome.tabs.sendMessage rejects with the no-receiver error. ensureInjectable swallowed its own injection error and there was no retry, so capture aborted. The manual button "worked" only because the user clicks seconds later, by which point the script is ready.

This also affected tabs that were already open before the extension was (re)loaded — static content_scripts don't run on pre-existing tabs, so they relied entirely on the one-shot programmatic injection.

Fix

sendToContentScript(tabId, message, { retries, delayMs }): send the message; on a no-receiver error, re-inject content.js and retry with a short backoff (up to 6 times), rethrowing any other error immediately. Used for the racy metrics call. isNoReceiverError is extracted and pure.

Verification

  • New unit tests (tests/capture.test.ts): first-try success, retry-then-succeed (asserts re-injection count), immediate rethrow of unrelated errors, give-up after exhausting retries, plus isNoReceiverError matching. 48 tests pass (was 42).
  • npm run typecheck, npm run lint, npm run format:check, npm run build green.
  • Confirmed the retry logic is present in the built dist/assets/editor.js.
  • Live confirmation (icon → auto-capture succeeds) to be done by @DCCA after reload — no runner exists for the live extension.

🤖 Generated with Claude Code

One-click capture fires immediately when the editor opens, before the
target tab's content script has registered its message listener — so
SB_GET_PAGE_METRICS rejected with "Could not establish connection.
Receiving end does not exist." ensureInjectable swallowed its own failure
and there was no retry, so the capture aborted (the manual button worked
only because the user clicks seconds later, once the script is ready).

Add sendToContentScript: send, and on a no-receiver error re-inject
content.js and retry with a short backoff up to N times. Use it for the
racy metrics call. This also fixes capturing tabs that were already open
before the extension was (re)loaded, which never received the static
content script.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@DCCA
DCCA merged commit 29d30a2 into main Jun 27, 2026
2 checks passed
@DCCA
DCCA deleted the fix/capture-content-script-race branch June 27, 2026 20:56
DCCA added a commit that referenced this pull request Jun 27, 2026
- CLAUDE.md: content.ts message set (SB_CAPTURE_*), capture.ts retry
  helpers/guards (activateTab, sendToContentScript, isTabsBusyError,
  isNoReceiverError), the on-page notice flow, and wslPath/feedback helpers.
- README: one-click capture, capture notice, and Copy for Claude Code in
  the feature list.
- todo: drop the removed popup from the manual QA list; add one-click +
  notice QA items.
- Add .docs/done/2026-06-27-one-click-capture-reliability/ change record
  (PRs #11-#14).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant