Skip to content

Unattended capture: host releases the capture lock on timeout while the extension keeps capturing, and drops the late link #69

Description

@wolfgang-aura

Impact: the native host frees the capture lock when its request timer fires, but nothing stops the capture the extension already started.

  1. A second capture starts on top of the first. A capture that outlives its timeout (default 5 min, --timeout up to 900 s) gets a timeout reply, and the CLI exits 1. The host has already deleted the pending entry, so it forwards the next request while the first capture still runs. That request is usually the caller's retry, so the same post gets published twice.
  2. The late link is lost. When the first capture publishes, its reply finds no pending entry, and replyToCli returns at line 95. The branch that logs viewUrl for a CLI that walked away (lines 98-108) never runs. The capsule exists on the Worker, but neither stdout nor the host log names it. Only the share list on x.com still has it.

#21 fixed the same failure for a CLI socket that closes mid-capture, and the comment above pending (lines 87-90) describes that fix. The request timer still deletes the entry. The header (lines 10-11) also promises one capture at a time.

The extension has no deadline of its own. captureShare clamps timeoutMs to 900 s (line 226) but uses it only for waitForContentScript (line 242, capped at 90 s). chrome.tabs.sendMessage (line 243) waits as long as the capture runs. The CLI comment at line 32, "The extension clamps the capture to 900s", is wrong.

Where:

  • native-host/sourcecapsule-host.mjs: request timer at 154-157; replyToCli returns at 95 and deletes at 97; comments at 10-11 and 87-90.
  • extension-src/background.js: captureShare, lines 226, 242 and 243.
  • scripts/sourcecapsule-capture.mjs:32.

Evidence: the real host and CLI on a private pipe (SOURCECAPSULE_PIPE). A stub browser holds capture #1 open, answers it after #2 is forwarded, then answers #2.

CLI #1 exit 1 after 1070ms, stdout: { "ok": false, "error": "timeout", "message": "No reply within 1000ms." }
forwarded to the extension while #1 still runs: [
  'capture-share https://x.com/a/status/1',
  'capture-share https://x.com/a/status/2'
]
CLI #2 exit 0, stdout: { "ok": true, "viewUrl": "https://share.example/c/second", "warnings": [] }
late #1 viewUrl recorded anywhere in the host log: false

Host log of the same run:

2026-10-06T17:42:22.919Z forwarding 1791308542917-dfp4al capture-share
2026-10-06T17:42:23.988Z forwarding 1791308543986-zvrquy capture-share

The missing extension deadline is traced from the code, not run in a browser.

Environment: main 72777ef (v1.6.8). Node on Windows, private named pipe, no browser.

Next action:

  • On timeout, answer the CLI but keep the pending entry, marked cliGone, until the extension replies. Add a hard cap so a dead worker cannot hold the lock forever.
  • Log a reply for an unknown id with its viewUrl instead of dropping it.
  • Race chrome.tabs.sendMessage in captureShare against timeoutMs. The existing finally then closes the capture window, which stops the capture.
  • Fix the comments at host lines 10-11 and CLI line 32.
  • Add a case to test/native-host.test.mjs: a reply after the timer must not let a second capture start, and its link must reach the log.

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

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions