For a few days, Slack would freeze whenever I clicked a channel. I’d get the rainbow , wait anywhere from 5 to 30 seconds, and then carry on until it happened again.

I tried quitting and reopening Slack, then “Clear Cache and Restart” from the Help menu. Neither helped. Eventually I exported the logs (Help > Troubleshooting > Show Logs in Finder). They turned up a couple of problems that looked promising, but it took a while to find the one causing the freezes.

Here’s what I was running:

Slack4.52.178 (Electron 44.4.5, Chromium 152)
macOS27.0 (26A428)
MachineMacBook Pro, Apple M4 Pro, 14 cores, 24 GB
External display4K BenQ, scaled to 1920x1080

What is in a Slack log bundle

The export had about two dozen files. I spent most of my time in these:

  • browser.log and rotations. This is Slack’s Electron main process. Window lifecycle, power events, display changes, and a stream of Sentry-style breadcrumbs.
  • webapp-console.log and rotations. The renderer. Everything the web app logs: API queue, socket manager, huddle SDK, navigation.
  • diagnostic.json. A one-shot hardware snapshot taken at export time.
  • environment.json, local-settings.json, gpu-info.html. Versions, feature flags, GPU state.
  • A .dmp crash minidump if Crashpad has one lying around.

The logs rotated at 5 MB. In my workspace, that worked out to roughly a day per file, and the bundle covered about four days.

Wrong turn 1: the huddle that never ended

I started by grouping repeated messages in webapp-console.log. These were at the top:

2014  warn: HUDDLE-SDK (T0XXXXXXXXX) Batch timing timeout
 133  info: [DND_V2] (T0XXXXXXXXX) Will check for changes in DND status again in 5 minutes
  81  info: [DND_V2] (T0XXXXXXXXX) Checking for changes in DND status for the following members: ...
  44  info: Store: REDUX_UPDATE_FROM_WEBAPP (T0XXXXXXXXX) Changed keys [unreads]

More than two thousand huddle warnings in one file, even though I hadn’t been in a huddle for days. Across three rotations, I counted 4,678. They appeared every 15 seconds, from the 19th until I quit Slack on the 23rd.

The first warning appeared just after I left a huddle on the 19th:

[10/19/26, 22:36:00:382] info: [HUDDLE-SDK] (T0XXXXXXXXX) Huddles SDK shutdown completed
[10/19/26, 22:36:00:382] info: [HUDDLE-CLIENT-MIDDLEW] User left huddle
[10/19/26, 22:36:00:382] info: [HUDDLES] Marking huddle as NOT_STARTED [T0XXXXXXXXX, C0XXXXXXXXX]
[10/19/26, 22:36:00:429] info: [HUDDLE-SDK] (T0XXXXXXXXX) Receive an audio output change event
[10/19/26, 22:36:00:430] error: Uncaught TypeError: Cannot read properties of undefined (reading 'destroy_audio_buffer')
[10/19/26, 22:36:01:789] warn: HUDDLE-SDK (T0XXXXXXXXX) Batch timing timeout

The SDK reported that shutdown was complete. Forty-seven milliseconds later, an audio output change event arrived, followed by an error about destroy_audio_buffer. I may have unplugged my headphones around then. It looked like the handler had tried to use something that had already been torn down. After that, the batching timer kept firing every 15 seconds for three and a half days.

That looked like a cleanup bug, and it was a reasonable suspect. But restarting Slack stopped the warnings without stopping the . Whatever was going on with the huddle timer, it didn’t explain the freezes after a restart.

Wrong turn 2: memory pressure

diagnostic.json also showed very little memory available:

{"hardware":{"total_memory_in_gb":24,"available_memory_gb":1,"uptime_hours":92.72,...}}

A sample in browser.log from a few minutes earlier reported "available_memory_gb": 0. The machine had been up for nearly four days. I checked its current memory use:

$ top -l 1 -n 0 | grep PhysMem
PhysMem: 23G used (4969M wired, 8294M compressor), 91M unused.

$ sysctl vm.swapusage
vm.swapusage: total = 18432.00M  used = 16835.38M  free = 1596.62M  (encrypted)

$ vm_stat | grep -E "Swapins|Swapouts"
Swapins:                                     6616380.
Swapouts:                                    8344820.

About 91 percent of the allocated swap was in use. With 16 KB pages, those counters worked out to roughly 100 GB moved in each direction since boot. Slack and its helpers accounted for only about 650 MB. Other processes were using much more:

ProcessFootprint
Docker Desktop VM8.1 GB resident, 13 GB compressed
WindowServer6.0 GB
Zed5.3 GB
gopls, 11 instances across worktrees~8 GB combined
25 idle CLI sessions, 17 of them restored at login and untouched for 3.8 days3.4 GB

That seemed plausible too. If Slack’s renderer had been paged out while I was working in the editor, switching back could mean waiting for it to be read from disk. Restarting Slack wouldn’t fix memory pressure caused by everything else on the machine.

I killed the stale CLI sessions, which freed about two gigabytes of swap. Slack still froze. Then I rebooted. Three minutes after boot:

$ sysctl vm.swapusage
vm.swapusage: total = 0.00M  used = 0.00M  free = 0.00M  (encrypted)

There was no swap in use. Slack’s renderer was at 0.5 percent CPU, and its socket had connected within a second of launch. The first channel switch still brought back the .

The machine had needed that cleanup, but it hadn’t fixed Slack. I went back to the logs.

What the logs cannot show you

In the fresh logs after the reboot, I looked for slow requests. Slack’s API queue records ENQUEUED, ACTIVE, and RESOLVED or REJECTED with a shared request ID. I used those to calculate how long requests took:

awk '
function ts(s,a){ split(s,a,/[\[\], :]+/); return a[4]*3600+a[5]*60+a[6]+a[7]/1000 }
/API-Q.*is ACTIVE/ { split($0,w," "); start[w[6]" "w[7]]=ts($0) }
/API-Q.*is (RESOLVED|REJECTED)/ { split($0,w," "); k=w[6]" "w[7];
  if (k in start) { d=ts($0)-start[k]; if (d>3) print d, w[7]; delete start[k] } }
' webapp-console.log

Across a full day of logs, none took more than three seconds. The websocket had disconnected twice in four days, both times when the machine went to sleep, and had reconnected in under a second each time.

There were no window.unresponsive breadcrumbs in browser.log either. Electron waits before reporting an unresponsive renderer, and Slack had an experiment called desktop_extended_unresponsive_timeout to extend that wait. The missing breadcrumbs didn’t tell me much about these shorter freezes.

The request timings weren’t explaining what I was seeing. I suspected the renderer was getting stuck during channel switches, but I needed something beyond the API logs to follow that up.

The clue I’d missed: 452 accessibility events

I took another look at the repeated messages in browser.log. I’d skimmed past this one earlier:

452  info: Breadcrumb: electron: app.accessibility-support-changed

There were 452 of these in one log rotation. The hourly counts were remarkably regular:

48 [10/22/26, 11
48 [10/22/26, 12
52 [10/22/26, 13
48 [10/22/26, 14
48 [10/22/26, 15
...
48 [10/22/26, 23
36 [10/23/26, 00
28 [10/23/26, 01

Around 48 an hour, even while I was asleep. The related settings entries came in pairs, always true. I found no false entries:

[10/22/26, 20:15:33:384] info: Store: SET_ACCESSIBILITY_SUPPORT_ENABLED true
[10/22/26, 20:15:33:387] info: Store: SET_ACCESSIBILITY_SUPPORT_ENABLED true
[10/22/26, 20:20:38:358] info: Store: SET_ACCESSIBILITY_SUPPORT_ENABLED true
[10/22/26, 20:20:38:364] info: Store: SET_ACCESSIBILITY_SUPPORT_ENABLED true
[10/22/26, 20:25:43:359] info: Store: SET_ACCESSIBILITY_SUPPORT_ENABLED true

Intervals between pairs, in seconds:

305 305 245 305 305 305 305 305 305 305 305

Most pairs were 305 seconds apart. Something appeared to be running on a timer and causing Electron to report that accessibility support was enabled.

Why that matters in Electron

Chromium enables accessibility support on demand.1 When a client requests it through the platform’s accessibility APIs, Chromium builds a tree describing the page’s controls, text, and relationships. Electron exposes changes to accessibility support through accessibility-support-changed and the app.accessibilitySupportEnabled property.2

Maintaining that tree adds work. The renderer has to update it as the page changes and send updates to the browser process.1 A Slack channel switch replaces a lot of content, so this seemed like a possible explanation for why switching channels had become so expensive. The logs alone couldn’t show how much of each freeze was spent on that work.

On my machine, accessibility support also seemed to stay enabled after the client went away. I saw repeated true entries without a corresponding false, and later testing showed that Slack needed a restart to recover.

I needed to find what was accessing Slack every five minutes or so.

The client

I started with the background apps that might inspect windows or notice when I switched apps:

Caffeine, cmux, Maccy, Raycast (5 procs), Shottr, Stats, Fathom

The process names didn’t narrow it down much. The full command lines were more useful:

$ ps -axo pid,command | grep -i fathom
1315  /Applications/Fathom.app/Contents/Resources/FathomMeetingMonitor null
        --platform zoom --platform google_meet --platform microsoft_teams
        --platform slack_huddle --green --max-in-flight-commands 16 --ax-messaging

Fathom is a meeting recorder, and its monitor was configured for Zoom, Meet, Teams, and Slack huddles. The combination of --platform slack_huddle and --ax-messaging made it my next suspect: it appeared to be using accessibility access to detect huddles in Slack.

That would explain why accessibility support was being enabled even though I wasn’t using a screen reader. A meeting recorder can request accessibility information too.

Suspected sequence: Fathom accesses Slack through the Accessibility API, enabling accessibility support and adding work during channel switches

Testing it

The command line was a clue. I tested it by quitting and reopening the apps:

1. Quit Fathom, then restart Slack. I quit Fathom, killed its monitor processes, and relaunched Slack. I switched through ten channels, including large ones. No .

2. Open Fathom again. A few minutes later, I briefly opened Fathom and quit it. Slack started beachballing on channel switches again. The log showed accessibility support being enabled when Fathom opened:

[10/23/26, 10:59:24:228] info: Store: SET_ACCESSIBILITY_SUPPORT_ENABLED        <- Slack relaunch, startup
[10/23/26, 11:03:06:355] info: Store: SET_ACCESSIBILITY_SUPPORT_ENABLED true   <- Fathom opened
[10/23/26, 11:03:06:357] info: Store: SET_ACCESSIBILITY_SUPPORT_ENABLED true

Quitting Fathom didn’t undo it. Restarting Slack cleared it again.

3. Compare the timing. The 305-second pattern in the logs was consistent with a background poll, including overnight when I wasn’t at the keyboard.

Being able to bring the freezes back by opening Fathom was the most useful result. I uninstalled it, and Slack has been fine since.

Why the earlier fixes didn’t help

Looking back, the failed fixes made more sense:

  • Restart Slack: it starts fresh, but Fathom can enable accessibility support again.
  • Clear Cache and Restart: the cache wasn’t what was triggering the problem.
  • Reboot: Fathom was a login item and started before Slack.
  • Free up memory: that helped the machine overall, but Slack still froze with no swap in use.

I don’t regret checking the huddle errors or the memory use. Both needed attention. But I’d spent most of my time on the loudest problems in the logs and overlooked the repeated accessibility events.

What I’d check next time

Check repeated accessibility events. If accessibility-support-changed appears at regular intervals, look for a background app accessing the Electron app. Full process command lines can give you clues that the app names don’t.

Clean request logs don’t rule out a UI freeze. Slack’s API timings looked fine throughout this. They told me about requests, not all the work involved in switching channels.

Try the suspected fix and check the original symptom. The huddle warnings and memory pressure both looked convincing. Restarting Slack and then rebooting helped me rule them out as the explanation for the freezes that remained.

Restart the affected app after removing the suspected client. In this case, quitting Fathom wasn’t enough. Slack recovered only after I restarted it too.

Appendix: side findings

I also found two issues that appeared unrelated to the channel-switching freezes:

  • Slack huddle SDK leak. destroy_audio_buffer TypeError during teardown when an audio device change races the shutdown, followed by a 15 second timer that never stops. Not yet reported to Slack at the time of writing; I will update this post when I do.
  • Raycast and nsattributedstringagent. 17 EXC_GUARD crash reports in two days for macOS’s rich text XPC service, all with Raycast as the responsible process, on macOS 27.0. Unrelated to Slack, but noisy.

Appendix: commands

These are the commands I used during the investigation. Only reading a system diagnostic report needed root access.

# Deduplicated view of a Slack log, numbers and IDs normalised
grep -hE " (warn|error): " webapp-console.log \
  | sed -E 's/^\[[^]]*\]//; s/[0-9a-f]{8,}/X/g; s/[0-9]{3,}/N/g' \
  | sort | uniq -c | sort -rn | head -40

# Accessibility toggles per hour
grep -h "SET_ACCESSIBILITY_SUPPORT_ENABLED" browser*.log \
  | grep -oE "^\[[0-9/]+, [0-9]{2}" | sort | uniq -c

# Interval between toggles, seconds
grep -h "SET_ACCESSIBILITY_SUPPORT_ENABLED" browser.log \
  | awk '{split($2,t,/[:\]]/); s=t[1]*3600+t[2]*60+t[3]+t[4]/1000;
          if (prev && s-prev>2) printf "%.0f ", s-prev; prev=s}'

# Who is holding memory, including compressed pages
top -l 2 -o mem -n 25 -stats pid,mem,cmprs,cpu,command | awk '/^PID/{c++} c==2'

# Full command lines of background helpers
ps -axo pid,pcpu,rss,command | grep -iE "raycast|maccy|shottr|fathom|stats|cmux"

# Sample a renderer's main thread. Note: /usr/bin/sample explicitly,
# a Python tool manager had shadowed `sample` on my PATH.
/usr/bin/sample "$(pgrep -f 'Slack Helper \(Renderer\)' | head -1)" 25 1 -file /tmp/slack.sample

References


  1. Chromium, Accessibility Overview. Sections “Command Line Options”, “Chromium’s multi-process architecture”, “Incremental tree updates”, and “The content layer”. ↩︎ ↩︎

  2. Electron, app API reference: accessibility-support-changed event and app.accessibilitySupportEnabled property. ↩︎