[BUG] FluidMeet: Teams meeting recording exhausts swap and freezes macOS (two hard power-offs required)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 10/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- macos, swift
- Domain
- desktop-dev, machine-learning, performance
Research direction
Start with MeetingSessionCoordinator.enqueuePersistence and MeetingSessionStore.atomicPrivateWrite, which the report's stack traces show rewriting the whole session blob on every save. MeetingAutoDetector's reason=busy loop and the ASR model switch in MeetingProcessingPipeline are the other hot spots. Done means reproducing the write volume with a long meeting and showing it drops, which needs a Teams meeting or a simulated session and profiling with Instruments.
Written by the indexing model from the issue text.
Description
Summary
Recording a Microsoft Teams meeting with FluidMeet drove my MacBook into swap exhaustion, forcing me to hard power-off the machine twice in one session. macOS never showed an "out of memory" dialog — instead the kernel suspended FluidVoice for swap exhaustion, then escalated to vm-compressor-space-shortage process kills. The app's own resident footprint was modest (295–397 MB); the damage came from file-backed writes and memory-compressor pressure.
This looks reproducible and appears to be a bug in the fork's session-persistence + model-loading path. Filing here since I don't own the app.
Environment
- Mac17,3 — Apple Silicon — 24 GB RAM — macOS 26.7.1 (25G241)
- FluidVoice 1.6.10-beta.7 (26) during the incident; 1.6.12-beta.1 (29) observed later
- Trigger app: Microsoft Teams (
com.microsoft.teams2) — "Fluid Meet" auto-detected and armed
What happened
| Attempt | App PID | Window | Outcome |
|---|---|---|---|
| #1 | 1289 | ~19:00 → 19:10 | kernel suspended FluidVoice for swap exhaustion → hard power-off |
| #2 | 1015 | ~19:15 → 19:28 | same, then vm-compressor-space-shortage kills → hard power-off |
After attempt #2 the kernel killed softwareupdated, assistantd, homed, SafariBookmarksSyncAgent, AccessibilityVisualsAgent, ScreenTimeAgent… Machine only recovered ~19:48.
Evidence — full bundle
📎 All raw evidence (sanitized): https://gist.github.com/arendon1/c8eebbf9658d7d99820166e1fdfb0046
01-Fluid-app.log— full app log (~/Library/Logs/Fluid/Fluid.log)02-Fluid-app-last2h.log— failure window only03-kernel-memory-events.txt— kernelmemorystatus/jetsam/compressor + ANE events04-diagnostic-report-excerpts.md— excerpts from the four Apple.diagreports generated in the window05-RCA.md— full RCA
1. macOS disk-write watchdog exceeded ~4x (.diag, PID 1015)
Writes: 2147.83 MB of file backed memory dirtied over 237 seconds (9066.51 KB/s)
Limit: 2147.48 MB per 86400s
Footprint: 294.52 MB -> 397.42 MB (+102.91 MB in 226s)
Pageins: 103,864 pages
CPU: 90 seconds cpu time over 142 seconds (63% cpu average)
Attempt #1's report shows the same 2147.83 MB but over 40,975 s at 52 KB/s — so the daily write budget was already saturated by long-running use; attempt #2 re-dirtied the same volume in 237 s (~170× faster).
2. Dominant stack = session persistence rewrite — 86/201 samples
MeetingSessionCoordinator.enqueuePersistence(_:)
└ MeetingSessionStore.save(_:)
└ MeetingSessionStore.writeSession(_:)
└ MeetingSessionStore.atomicPrivateWrite(_:to:)
└ Data.write(to:options:)
└ writeToFileAux(path:buffer:options:attributes:reportProgress:)
└ write()
A full-buffer write of the session on every enqueuePersistence call. Write volume scales with session size × persistence frequency.
3. Secondary stack = CoreML on-device AOT force-respecialization — 44/201 samples
MLE5ProgramLibrary _programLibraryHandleWithForceRespecialization:error:
└ createProgramLibraryHandleWithRespecialization:error:
└ e5rt_e5_compiler_compile_from_ir_program
└ MILCompilerForANE / MILCompilerForBnns
└ bnns::GraphCompile
└ MIL::Blob::FileWriter::AppendData → fwrite → write
CoreML is force-respecializing an already-compiled model, serializing the full MIL program as text and dumping fp16 weights into ~/Library/Caches/com.FluidApp.app/com.apple.e5rt.e5bundlecache/. App log confirms the cost — each first load of a distinct model takes ~15.5 s.
4. Model-switch thrash in a 16-second window (PID 1015)
19:15:05.412 ASR INIT START -> prepare Parakeet TDT v3
19:15:20.909 "prepared successfully in 15.50s" <- COLD AOT compile #1
19:17:09.381 MeetingProcessingPipeline: final processing selected backend=local-parakeet-tdt-v2-nemotron-3-v1
19:17:09.501 PrivateAIProvider unloaded cached runtime model reason=meeting processing
19:17:10.626 prepare Parakeet TDT v2 (English Only)
19:17:26.136 "Models ready" <- COLD AOT compile #2
19:17:26.272 ASR INIT START again -> prepare Parakeet TDT v3
19:17:26.499 "Models ready" <- warm reload (0.23 s)
The pipeline unloads the resident streaming model, cold-compiles a second ASR model for final processing, then reloads the streaming model — all while persistence is writing.
5. Memory-compressor death spiral
19:03 compressor_size ≈ 745,137 pages (~11.9 GB)
19:10 memorystatus: suspending FluidVoice [1289] due to swap exhaustion -> shutdown #1
19:28 compressor_size ≈ 811,686 pages (~13.0 GB of 24 GB)
19:28 memorystatus: suspending FluidVoice [1015] due to swap exhaustion
19:28 {"compressor_exhausted": 1, ...} killing due to "vm-compressor-space-shortage" (x16 kills)
-> shutdown #2
6. Contributing — ANE program retention
12× in 19:00–19:49:
ANE0: ANE_SaveState_block_invoke: WARN:: clientname:FluidVoice is keeping programs loaded and not
freeing - isPowered: 1 Programs Open:1 Wired-memory: 30834688 B
≈29.4 MB wired each, never released on model switch.
7. Contributing — MeetingAutoDetector busy-loop
confirm-rejected reason=busy bundle=com.microsoft.teams2 fires every ~2 s for hours (1,152 occurrences in the 19:xx window alone). This kept the app in a hot state and burned the sustained 60%+ CPU. Also: 19:01:29 [MeetingLive] Live captions dropped audio and resynced.
Root cause chain
Teams meeting detected
-> MeetingSessionCoordinator starts persisting session
-> MeetingSessionStore.atomicPrivateWrite rewrites the whole session blob on every enqueue
-> MeetingProcessingPipeline switches final-processing backend to v2-nemotron
-> unloads resident v3
-> cold AOT-compiles v2 (CoreML force-respecialization -> MIL text + fp16 weight dump)
-> reloads v3
-> each cycle dirties GBs of file-backed memory
-> 2.1 GB dirtied in 237 s -> exceeds the macOS disk-write rate limit
-> ANE programs retained; compressor grows to ~13 GB
-> swap exhausted -> kernel SUSPENDS FluidVoice (attempt #1)
-> relaunch -> same cycle, faster
-> swap exhausted -> vm-compressor-space-shortage kills (attempt #2)
Suggested fixes
P0
- Make session persistence incremental (append-only journal or SQLite/WAL); debounce/coalesce saves; never rewrite the whole blob per enqueue; never store raw audio in the persisted JSON.
- Eliminate CoreML
forceRespecialization— pinMLModelConfiguration(computeUnits/variant) so the compiled e5rt bundle is reused; ship precompiled.mlmodelcwhere possible. - Stop loading a second ASR model for final processing while streaming. Reuse one model end-to-end, or run final processing in a separate, memory-capped helper after releasing the streaming model.
- Fix the
MeetingAutoDetectorbusy-loop (reason=busyshould back off / clear). - Release ANE programs explicitly on model switch.
P1
6. Add an in-app memory watchdog (task_vm_info footprint + compressor via host_statistics64); if compressor > ~50% RAM, stop recording, flush, release models, and warn — GUI apps get no jetsam warning before suspension.
7. Don't run streaming ASR + final ASR + persistence concurrently on one async executor (the diag shows all three stacked under a single completeTaskWithClosure).
Note on raw .diag files
The four Apple reports live in /Library/Logs/DiagnosticReports/ (660 root:_analyticsusers) and need admin access to copy. Key excerpts are in the gist; happy to attach the raw files if you tell me the best way.
Reported by an external user; app is not mine. Happy to run any additional diagnostics you need.
- Dominant language
- Swift
- Stars
- 11.9k
- Forks
- 863
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 28
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from altic-dev/FluidVoice
-
bug
Difficulty 4/5 1-2 days Newbie friendliness 35/100
altic-dev/FluidVoice#1138 · 2 comments ·
Maintainers usually reply within 1 day
-
[FEATURE] FluidMeet: recover excluded microphone audio after the call with offline echo cancellationOpen
Difficulty 5/5 Over a week Newbie friendliness 15/100
altic-dev/FluidVoice#1136 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 30/100
altic-dev/FluidVoice#1135 · 1 comment ·
Maintainers usually reply within 1 day
-
[BUG]Openbug
Difficulty 4/5 3-5 days Newbie friendliness 15/100
altic-dev/FluidVoice#1133 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 4/5 3-5 days Newbie friendliness 45/100
altic-dev/FluidVoice#1119 · 2 comments ·
Maintainers usually reply within 1 day
All issues in altic-dev/FluidVoice
Similar issues
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 60/100
meshtastic/Meshtastic-Apple#2603 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
bug Windows
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
fyi-ecosystem platform-ios team-ios
Difficulty 1/5 Under an hour Newbie friendliness 70/100
flutter/flutter#194348 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 88/100
freestylefly/WeChatBridge#44 ·
Maintainers usually reply within 1 day