macOS: target thumbnail screenshot can hang, leaving the screen-recording indicator (and "Your screen is being observed") stuck — even after Cap quits
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 45/100
調査の方向性
The issue is in the thumbnail capture code in apps/desktop/src-tauri/src/thumbnails/mod.rs and mac.rs. The capture_sample_buf call hangs, blocking the command. Start by examining the async flow and the block_in_place usage in recording.rs. Look for how to add a timeout or switch to using SCStream with explicit stopCapture. Test by running the app and monitoring replayd logs to see if a hung session is created and if the fix releases it. 'Done' means the screen-recording indicator clears after the app quits, even after a simulated hang.
索引モデルが issue の本文から書いたものです。
説明
Summary
On launch (no recording started), Cap captures display/window thumbnails with SCScreenshotManager. One of those screenshot requests never completed. While it was pending, macOS kept a screen-capture session open for Cap, so the purple screen-recording indicator stayed on and the lock screen showed "Your screen is being observed".
After Cap quit, replayd ran its per-client cleanup (stopAllStreamsWithError), but the stuck screenshot stream was not released. The indicator kept attributing screen capture to so.cap.desktop until I force-restarted replayd.
The hang itself looks like a ScreenCaptureKit/macOS issue, and so does the missing cleanup on exit. On Cap's side, the Tauri app awaits each thumbnail with no timeout. Once a request hangs, nothing on Cap's side can end it.
Environment
- Cap desktop 0.6.0 (Tauri app,
/Applications/Cap.app, bundle idso.cap.desktop) - macOS 27.0 (build 26A428)
- MacBookPro18,3 (Apple Silicon), single built-in display
Steps to reproduce
- Make sure Cap is not running and no screen-recording indicator is shown.
- In Terminal, start watching the indicator and capture sessions:
log stream --style compact --predicate '(process == "ControlCenter" AND category == "sensor-indicators") OR (process == "replayd" AND eventMessage CONTAINS "SLContentStream")' - Launch Cap. Do not start a recording.
- Count
SLContentStream start:againstSLContentStream stop:lines, and watch whetherActive activity attributionsstays at["scr:so.cap.desktop"]. - Quit Cap and check whether the indicator clears.
This reproduced on my first attempt. I have not measured how often it happens.
What I observed
Clean launch at 08:06:18, no recording:
08:06:18.441 SLContentStream start -> 08:06:18.459 stop (ok)
08:06:18.566 SLContentStream start -> 08:06:18.567 stop (ok)
08:06:18.679 SLContentStream start -> 08:06:18.681 stop (ok)
08:06:18.743 SLContentStream start -> never stopped <-- hung
08:06:46.545 SLContentStream start -> 08:06:46.550 stop (ok)
08:07:10.690 SLContentStream start -> 08:07:10.702 stop (ok)
Every start came from +[SCScreenShotSession captureSLContentStreamScreenshot:withCompletionHandler:], so these are screenshot sessions, not recording streams. From 08:06:18.743 on, ControlCenter reported:
Active activity attributions changed to ["scr:so.cap.desktop"]
Quit at 08:07:39:
08:07:39.443 replayd invalidated because the client process (pid 23425) either cancelled the connection or exited
08:07:39.444 replayd -[RPClient stopAllStreamsWithError:stopReason:] stopReason=0
08:08:06.150 ControlCenter Sorted active attributions from SystemStatus update: [[scr] Cap (so.cap.desktop)] <-- Cap is gone, indicator still on
The indicator only cleared after replayd was killed. launchd then restarted it.
Earlier, in normal use (before I knew the cause)
- ControlCenter attributed 2–3 concurrent
scrsessions to Cap for about 17 hours. Cap's own log (~/Library/Logs/so.cap.desktop) shows only two short studio recordings (5–6 s each) in that period, and both pipelines shut down cleanly. - The long-lived sessions started shortly after a sleep/wake. Cap logged
System woke from sleep; scheduling recovery refreshat that point. - Cap exited normally (
Exit cleanup completed elapsed_ms=11). Onescrattribution then stayed for more than 3 hours untilreplaydwas restarted.
My guess is that each thumbnail refresh (for example after wake) can leak one more hung session. I could not confirm this because the detailed logs from that period had already rotated.
Relevant code (tag cap-v0.6.0)
- Thumbnails are captured one by one and each one is awaited with no timeout:
apps/desktop/src-tauri/src/thumbnails/mod.rs#L89-L136 - The capture call is
sc::ScreenshotManager::capture_sample_buf(...).await, at a flat 320x180:
apps/desktop/src-tauri/src/thumbnails/mac.rs#L22-L42 list_*_with_thumbnailsrun this throughblock_in_place+block_on, so a hung request also blocks the command:
apps/desktop/src-tauri/src/recording.rs#L1122-L1157- The GPUI app already records that
capture_sample_bufcan hang for some sizes without an error ("never resolves -- no error, the future hangs"):
apps/desktop-gpui/src/target_thumbnails.rs#L423-L424
In my case the request used the flat 320x180 size that comment treats as safe, and it still hung.
What I don't know
- Which display or window the hung request was for.
replaydlogs the filter as<private>. - Whether this reproduces on macOS 26.x, or only on 27.0.
Possible mitigations
- Put a per-thumbnail timeout around
capture_sample_buf(for example 2 s) and skip that target on timeout. This keeps the picker from blocking. It probably does not clear the indicator, becauseSCScreenshotManagerhas no cancel API and dropping the future leaves the system session open. - For thumbnails, use a short-lived
SCStream(the existingscap_screencapturekit::Capturer): take the first frame, then callstopCaptureexplicitly, including on timeout. Cap then owns the session's lifetime. I have not verified thatstopCapturereleases a stream that never delivered a frame. - The missing cleanup on exit (the screenshot session survives the client's
stopAllStreams) looks like a macOS bug. It is probably worth a Feedback Assistant report to Apple as well.
Workaround for affected users: quit Cap, then reboot, or run killall -9 replayd (launchd restarts it).
- 主要言語
- Rust
- スター
- 22.5k
- フォーク
- 1.9k
- 平均マージ
- 7時間 43分
- マージ済み PR(30日)
- 80
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
CapSoftware/Cap のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
CapSoftware/Cap#2305 · コメント 2 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
CapSoftware/Cap#1714 · コメント 3 件 ·
-
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
CapSoftware/Cap#2340 · コメント 1 件 ·
-
難易度 3/5 1〜2日 初心者へのやさしさ 58/100
CapSoftware/Cap#2329 · コメント 2 件 ·
-
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
CapSoftware/Cap#2328 · コメント 1 件 ·
CapSoftware/Cap の issue をすべて見る
似ている issue
-
bug github_actions
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
registrystack/registry-stack#1393 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
longbridge/gpui-kit#3223 ·
-
bug engine
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
rocky-data/rocky#2181 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
oasisprotocol/oasis-sdk#2523 ·
-
[indexer] [QA] Add a focused test for the new NonRetryableError / assertSocketAlive() behavior. オープンbot:ai-assisted component:indexer QA-roadmap status:untriaged
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
midnightntwrk/midnight-indexer#1557 ·