You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A take that ran 23 min 20 s produced a 74-second file.
The write path died about 75 seconds in. ScreenCaptureKit kept capturing and kept delivering healthy frames for another 22 minutes. The HUD kept counting up the whole time. Nothing told the user the recording had effectively already ended — the loss was completely silent and was only discoverable afterwards, from the filesystem and the unified log.
I believe v1.10.0 is affected too: nothing in that release or on main since looks like it addresses this, and #363 explicitly scoped macOS out.
Environment: OpenScreen 1.9.6 (com.etiennelescot.openscreen), macOS 26.6.2 (25G83), Apple M4, arm64. Screen + USB_MIC capture, 1920×1080, no camera.
Note on timestamps. Absolute dates and clock times are redacted throughout. Times are given as offsets from the take's start (T0). In the quoted log lines only the leading timestamp has been substituted with its T0 offset — everything after it is verbatim from log show --predicate 'process == "replayd"'. Filenames are redacted where they embed a timestamp (see §1).
1. The output file stops at 74 s but is not truncated
The take's recording-<redacted>.mp4 under ~/Library/Application Support/openscreen/recordings/. The numeric suffix is redacted because it is an epoch-milliseconds start timestamp, which is how T0 is known:
fragmented MP4: ftyp, wide, moov at offset 1410192, then 73 moof and 75 mdat boxes — roughly one fragment per second of the 74 s take
the box lengths sum to exactly the file size (74953463 bytes) → the writer stopped cleanly at a fragment boundary; this is not a partial or torn write
ffmpeg -v warning -i f -f null - → exit 0, zero errors
ffprobe -count_frames → 3998 frames = 54.0 fps across 74 s, consistent end to end
mtime froze at T0+75 s and never advanced again
there is no .session.json beside it → the save path never ran for this take. The immediately preceding take has both a .session.json and a project file, so this is specific to the failed take, not to the build.
2. replayd reports the session healthy for the entire 23 minutes
266Health: heartbeats between T0+71 s and T0+23:16, one every ~5 s, all from the same session:
screenframeCount251–297 per 5 s window (≈50–59 fps) throughout — no gap, no degradation
audioFrameCount245–255 per window throughout
_screenTimeDriftSeconds=0.00000000000000000000 on all 266
First heartbeat after the file stopped growing (T0+71 s):
3. The session was only torn down when the client process exited
At T0+23:20, four seconds after the last heartbeat:
[T0+23:20] Df replayd[1177:d87a2a] [INFO] -[RPClient stop:streamID=<redacted>] Success stopping stream
[T0+23:20] Df replayd[1177:d87a2a] [INFO] -[SCScreenCaptureSession dealloc] self=0x8823b86e0
[T0+23:20] Df replayd[1177:d87a2a] [INFO] -[SCCaptureSession dealloc] session=0x883624000
[T0+23:20] Df replayd[1177:d87a2a] [com.apple.ReplayKit:RPDaemon] -[AVCaptureDeviceInput dealloc] <AVCaptureDeviceInput: 0x881a1c720 [USB_MIC]>
[T0+23:20] Df replayd[1177:d87a2a] [com.apple.xpc:connection] [0x8830fca80] invalidated because the client process (pid 54137) either cancelled the connection or exited
The producer side was alive and healthy from T0 to T0+23:20. The consumer stopped persisting at T0+75 s.
4. Ruled out: this was not storage- or OS-level loss
I checked every avenue by which the missing 22 minutes could still have existed somewhere:
no file larger than 1 MB written anywhere on the data volume between T0+75 s and T0+23:20 (only unrelated third-party apps)
/private/var/vm/ contains no swapfiles at all — nothing was paged out
no core dumps in /cores/
tmutil listlocalsnapshots / → no local APFS snapshots, so no filesystem-level restore point
lsof +L1 → no deleted-but-still-open handles, so nothing was readable off a live fd
the recordings/ directory's own mtime never advanced after the take began → no file was created there and later removed
So the frames produced after T0+75 s were delivered to the app and dropped. They were never written to any medium. This is not a data-recovery problem; it is a silent-failure problem.
Those three are Windows, and the symptom is a hang on stop ("still hangs on stop", "timed out waiting for native windows capture to stop", "video is not saving").
This is macOS, and the failure is mid-recording and silent: capture stays healthy, the writer dies, and the UI keeps counting for 22 minutes. The user gets no signal that the take ended. I could not find a macOS report for this, which is why I am filing one.
#338 is the only reason any bytes survived — thank you. Without fragmented MP4 this take would have been an unreadable 0-second file instead of a clean, fully decodable 74-second one. But #338 says of itself: "What it does not change: the freeze. The user still sees a recording that stops on its own. That stays open." This is that open case, on macOS, at 23 minutes.
#363 scoped macOS out: "macOS fragments too and needs the same treatment, but it has no already-exited fast path and an unguarded stdin write, so it is its own change." It also listed under Deliberately not in this PR: "Detecting the mid-recording exit / stopping the HUD timer. The HUD does keep counting past a dead helper (observed: 02:12)." The observation there was 2 min 12 s; mine is 23 min 20 s. This report is the macOS evidence for both halves of that deferral — the salvage path and the dead-helper detection.
Impact
22 of 23 minutes of an unreproducible recording were lost. Because the HUD looked healthy throughout, there was no opportunity to notice and restart.
What should not happen is 22 minutes of HUD counting over a dead writer with no user-visible signal, followed by a take that silently does not exist.
Ideally the two outcomes are separable in the field: #363 notes the helper already emits a container field nobody read, which is what makes "was this file supposed to survive a kill?" answerable. A recovered: true flag on macOS would let a bug report distinguish a clean stop from a salvaged one here too.
To Reproduce
I cannot reproduce this on demand — it happened once, in a single 23-minute take, and I have no reliable trigger. This report is a forensic reconstruction from the artefacts that survived, not a reproduction.
What the artefacts pin down is the failure shape, which should be enough to look for it directly:
Observe: the .mp4 stops growing, replayd keeps logging healthy Health: heartbeats at ~55 fps, the HUD keeps counting, and no .session.json is ever written.
The cheap instrumentation to confirm it in the field: compare the output file's mtime against elapsed recording time. A file whose mtime is minutes behind the HUD is a dead writer, and today nothing performs that comparison.
OS
macOS
OS Version
26.6.2 (build 25G83)
Device Type
Desktop
Other Device
Mac mini (Apple M4, arm64)
Additional context
App version at the time of the failure: 1.9.6. Since upgraded to 1.10.0; not re-tested, as there is no reproduction.
Everything quoted above comes from log show --predicate 'process == "replayd"' scoped to the take's window; the 266-heartbeat count is over that whole window, not a sample.
Search existing issues
Describe the bug
A take that ran 23 min 20 s produced a 74-second file.
The write path died about 75 seconds in. ScreenCaptureKit kept capturing and kept delivering healthy frames for another 22 minutes. The HUD kept counting up the whole time. Nothing told the user the recording had effectively already ended — the loss was completely silent and was only discoverable afterwards, from the filesystem and the unified log.
I believe v1.10.0 is affected too: nothing in that release or on
mainsince looks like it addresses this, and #363 explicitly scoped macOS out.Environment: OpenScreen 1.9.6 (
com.etiennelescot.openscreen), macOS 26.6.2 (25G83), Apple M4, arm64. Screen +USB_MICcapture, 1920×1080, no camera.1. The output file stops at 74 s but is not truncated
The take's
recording-<redacted>.mp4under~/Library/Application Support/openscreen/recordings/. The numeric suffix is redacted because it is an epoch-milliseconds start timestamp, which is how T0 is known:ftyp,wide,moovat offset 1410192, then 73moofand 75mdatboxes — roughly one fragment per second of the 74 s takeffprobe: duration 74.0 s, 1920×1080, h264r_frame_rate=54, + aac 48 kHz stereoffmpeg -v warning -i f -f null -→ exit 0, zero errorsffprobe -count_frames→ 3998 frames = 54.0 fps across 74 s, consistent end to end.session.jsonbeside it → the save path never ran for this take. The immediately preceding take has both a.session.jsonand a project file, so this is specific to the failed take, not to the build.2.
replaydreports the session healthy for the entire 23 minutes266
Health:heartbeats between T0+71 s and T0+23:16, one every ~5 s, all from the same session:screenframeCount251–297 per 5 s window (≈50–59 fps) throughout — no gap, no degradationaudioFrameCount245–255 per window throughout_screenTimeDriftSeconds=0.00000000000000000000on all 266First heartbeat after the file stopped growing (T0+71 s):
Last one, 22 minutes later (T0+23:16):
3. The session was only torn down when the client process exited
At T0+23:20, four seconds after the last heartbeat:
The producer side was alive and healthy from T0 to T0+23:20. The consumer stopped persisting at T0+75 s.
4. Ruled out: this was not storage- or OS-level loss
I checked every avenue by which the missing 22 minutes could still have existed somewhere:
/private/var/vm/contains no swapfiles at all — nothing was paged out/cores/tmutil listlocalsnapshots /→ no local APFS snapshots, so no filesystem-level restore pointlsof +L1→ no deleted-but-still-open handles, so nothing was readable off a live fdrecordings/directory's own mtime never advanced after the take began → no file was created there and later removedSo the frames produced after T0+75 s were delivered to the app and dropped. They were never written to any medium. This is not a data-recovery problem; it is a silent-failure problem.
Why this is not #252 / #292 / #327
Those three are Windows, and the symptom is a hang on stop ("still hangs on stop", "timed out waiting for native windows capture to stop", "video is not saving").
This is macOS, and the failure is mid-recording and silent: capture stays healthy, the writer dies, and the UI keeps counting for 22 minutes. The user gets no signal that the take ended. I could not find a macOS report for this, which is why I am filing one.
Relation to #338 and #363
#338 is the only reason any bytes survived — thank you. Without fragmented MP4 this take would have been an unreadable 0-second file instead of a clean, fully decodable 74-second one. But #338 says of itself: "What it does not change: the freeze. The user still sees a recording that stops on its own. That stays open." This is that open case, on macOS, at 23 minutes.
#363 scoped macOS out: "macOS fragments too and needs the same treatment, but it has no already-exited fast path and an unguarded stdin write, so it is its own change." It also listed under Deliberately not in this PR: "Detecting the mid-recording exit / stopping the HUD timer. The HUD does keep counting past a dead helper (observed: 02:12)." The observation there was 2 min 12 s; mine is 23 min 20 s. This report is the macOS evidence for both halves of that deferral — the salvage path and the dead-helper detection.
Impact
22 of 23 minutes of an unreproducible recording were lost. Because the HUD looked healthy throughout, there was no opportunity to notice and restart.
Expected behavior
One of:
What should not happen is 22 minutes of HUD counting over a dead writer with no user-visible signal, followed by a take that silently does not exist.
Ideally the two outcomes are separable in the field: #363 notes the helper already emits a
containerfield nobody read, which is what makes "was this file supposed to survive a kill?" answerable. Arecovered: trueflag on macOS would let a bug report distinguish a clean stop from a salvaged one here too.To Reproduce
I cannot reproduce this on demand — it happened once, in a single 23-minute take, and I have no reliable trigger. This report is a forensic reconstruction from the artefacts that survived, not a reproduction.
What the artefacts pin down is the failure shape, which should be enough to look for it directly:
.mp4stops growing,replaydkeeps logging healthyHealth:heartbeats at ~55 fps, the HUD keeps counting, and no.session.jsonis ever written.The cheap instrumentation to confirm it in the field: compare the output file's mtime against elapsed recording time. A file whose mtime is minutes behind the HUD is a dead writer, and today nothing performs that comparison.
OS
macOS
OS Version
26.6.2 (build 25G83)
Device Type
Desktop
Other Device
Mac mini (Apple M4, arm64)
Additional context
log show --predicate 'process == "replayd"'scoped to the take's window; the 266-heartbeat count is over that whole window, not a sample.