Skip to content

Instantly share code, notes, and snippets.

@tkafka
Last active October 8, 2025 14:24
Show Gist options
  • Save tkafka/e3eb63a5ec448e9be6701bfd1f1b1e58 to your computer and use it in GitHub Desktop.
Save tkafka/e3eb63a5ec448e9be6701bfd1f1b1e58 to your computer and use it in GitHub Desktop.
Detect Electron apps on mac where the Electron hasn't yet been updated to fix the system wide lag

Electron Apps Causing System-Wide Lag on Tahoe

This script detects apps with not yet updated versions of Electron.

Repo: https://github.com/tkafka/detect-electron-apps-on-mac

See:

Fixed versions:

  • 36.9.2
  • 37.6.0
  • 38.2.0
  • 39.0.0
  • and all above 39

Temporary workaround:

Run

launchctl setenv CHROME_HEADLESS 1

on every system start. The CHROME_HEADLESS flag has a side effect of disabling Electron app window shadows, which makes them ugly, but also stops triggering the issue.

Example output

(as of 1st oct 2025 - it lists all electron apps, but none shows the ✅ checkmark so far)

❌ OpenMTP.app: Electron 18.3.15 (Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework)
❌ DaVinci Resolve.app: Electron 36.3.2 (Contents/Applications/Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework)
❌ Electron.app: Electron 36.3.2 (Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework)
❌ Visual Studio Code.app: Electron 37.3.1 (Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework)
❌ Cursor.app: Electron 34.5.8 (Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework)
❌ Windsurf.app: Electron 34.4.0 (Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework)
❌ Claude.app: Electron 36.4.0 (Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework)
❌ Signal.app: Electron 38.1.2 (Contents/Frameworks/Electron Framework.framework/Electron Framework)
❌ Figma Beta.app: Electron 37.5.1 (Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework)
❌ Beeper Desktop.app: Electron 33.2.0 (Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework)
❌ Slack.app: Electron 38.1.2 (Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework)

EDIT 2025-10-03: Congrats to Signal being first!

✅ Signal.app (Electron 38.2.0) - Contents/Frameworks/Electron Framework.framework/Electron Framework

A bit of promo

If you'd appreciate a visual (Tufte-like) hour by hour forecast for iOS/Apple Watch/mac with nice widgets, I made one - check out 🌦️ Weathergraph.

Thanks! Tomas

# Detect affected Electron versions
mdfind "kMDItemFSName == '*.app'" | while read app; do
electronFiles=$(find "$app" -name "Electron Framework" -type f 2>/dev/null)
if [[ -n "$electronFiles" ]]; then
appName=$(basename "$app")
while IFS= read -r filename; do
electronVersion=$(strings "$filename" | grep "Chrome/" | grep -i Electron | grep -v '%s' | sort -u | cut -f 3 -d '/')
if [[ -n "$electronVersion" ]]; then
IFS='.' read -r major minor patch <<< "$electronVersion"
relativePath=$(echo "$filename" | sed "s|$app/||")
if [[ $major -gt 39 ]] || \
[[ $major -eq 39 && $minor -ge 0 ]] || \
[[ $major -eq 38 && $minor -gt 2 ]] || \
[[ $major -eq 38 && $minor -eq 2 && $patch -ge 0 ]] || \
[[ $major -eq 37 && $minor -gt 6 ]] || \
[[ $major -eq 37 && $minor -eq 6 && $patch -ge 0 ]] || \
[[ $major -eq 36 && $minor -gt 9 ]] || \
[[ $major -eq 36 && $minor -eq 9 && $patch -ge 2 ]]; then
echo "$appName: Electron $electronVersion ($relativePath)"
else
echo "$appName: Electron $electronVersion ($relativePath)"
fi
break
fi
done <<< "$electronFiles"
fi
done
# Directly detect Electron apps using the _cornerMask override - thanks avarayr!
if [[ ! -f $(which rg) ]]; then
echo "❌ this script requires ripgrep, you can install it with: brew install ripgrep"
exit 1
fi
mdfind "kMDItemFSName == '*.app'" | while read app; do
electronFiles=$(find "$app" -name "Electron Framework" -type f 2>/dev/null)
if [[ -n "$electronFiles" ]]; then
appName=$(basename "$app")
while IFS= read -r filename; do
if [[ -f "$filename" ]]; then
ev=$(rg -a -m1 -o -r '$1' 'Chrome/.*Electron/([0-9]+(\.[0-9]+){1,3})' -- "$filename" 2>/dev/null)
[ -z "$ev" ] && ev=$(rg -a -m1 -o -r '$1' 'Electron/([0-9]+(\.[0-9]+){1,3})' -- "$filename" 2>/dev/null)
relativePath=$(echo "$filename" | sed "s|$app/||")
if rg -a -q -F "_cornerMask" -- "$filename" 2>/dev/null; then
echo "$appName (Electron ${ev:-unknown}) - $relativePath"
else
echo "$appName (Electron ${ev:-unknown}) - $relativePath"
fi
break
fi
done <<< "$electronFiles"
fi
done
@devnoname120
Copy link

@nilicule Thanks I just fixed it

@tkafka
Copy link
Author

tkafka commented Oct 1, 2025

@bartjakobs Thank! Fixed it!

@avarayr
Copy link

avarayr commented Oct 1, 2025

@tkafka great work!

To be pedantic, the electron version doesn't inherently mean the app has the issue.

Some companies use custom, forked versions of electron (I believe Slack does) and they can cherry pick the fix

here's a more reliable script that scans for _cornerMask in the binary content, which is the definitive culprit.

for f in /Applications/*/Contents/Frameworks/Electron\ Framework.framework/Versions/A/Electron\ Framework; do
  app=${f#/Applications/}; app=${app%%/Contents/*}

  # Get Electron version
  ev=$(rg -a -m1 -o -r '$1' 'Chrome/.*Electron/([0-9]+(\.[0-9]+){1,3})' -- "$f")
  [ -z "$ev" ] && ev=$(rg -a -m1 -o -r '$1' 'Electron/([0-9]+(\.[0-9]+){1,3})' -- "$f")

  # Ripgrep the binary for _cornerMask
  if rg -a -q -F "_cornerMask" -- "$f"; then
    echo -e "\033[31m❌ $app \033[2m(Electron ${ev:-})\033[0m"
  else
    echo -e "\033[32m✅ $app \033[2m(Electron ${ev:-})\033[0m"
  fi
done
  • requires ripgrep

@NetOpWibby
Copy link

@tkafka great work!

To be pedantic, the electron version doesn't inherently mean the app has the issue.

Some companies use custom, forked versions of electron (I believe Slack does) and they can cherry pick the fix

here's a more reliable script that scans for _cornerMask in the binary content, which is the definitive culprit.

for f in /Applications/*/Contents/Frameworks/Electron\ Framework.framework/Versions/A/Electron\ Framework; do
  app=${f#/Applications/}; app=${app%%/Contents/*}

  # Get Electron version
  ev=$(rg -a -m1 -o -r '$1' 'Chrome/.*Electron/([0-9]+(\.[0-9]+){1,3})' -- "$f")
  [ -z "$ev" ] && ev=$(rg -a -m1 -o -r '$1' 'Electron/([0-9]+(\.[0-9]+){1,3})' -- "$f")

  # Ripgrep the binary for _cornerMask
  if rg -a -q -F "_cornerMask" -- "$f"; then
    echo -e "\033[31m❌ $app \033[2m(Electron ${ev:-})\033[0m"
  else
    echo -e "\033[32m✅ $app \033[2m(Electron ${ev:-})\033[0m"
  fi
done
  • requires ripgrep

Running this has shown me that of all the Electron apps I have installed, only Tape lacks the slowness issue.

Thanks for this!

@poyhen
Copy link

poyhen commented Oct 1, 2025

@tkafka great work!

To be pedantic, the electron version doesn't inherently mean the app has the issue.

Some companies use custom, forked versions of electron (I believe Slack does) and they can cherry pick the fix

here's a more reliable script that scans for _cornerMask in the binary content, which is the definitive culprit.

for f in /Applications/*/Contents/Frameworks/Electron\ Framework.framework/Versions/A/Electron\ Framework; do
  app=${f#/Applications/}; app=${app%%/Contents/*}

  # Get Electron version
  ev=$(rg -a -m1 -o -r '$1' 'Chrome/.*Electron/([0-9]+(\.[0-9]+){1,3})' -- "$f")
  [ -z "$ev" ] && ev=$(rg -a -m1 -o -r '$1' 'Electron/([0-9]+(\.[0-9]+){1,3})' -- "$f")

  # Ripgrep the binary for _cornerMask
  if rg -a -q -F "_cornerMask" -- "$f"; then
    echo -e "\033[31m❌ $app \033[2m(Electron ${ev:-})\033[0m"
  else
    echo -e "\033[32m✅ $app \033[2m(Electron ${ev:-})\033[0m"
  fi
done
  • requires ripgrep

this one didnt detect signal compared to OP

@rrrix
Copy link

rrrix commented Oct 1, 2025

Here's a 1-liner copy & paste-able version of devnoname120's comment.

Use with a ZSH Shell (e.g. Terminal or iTerm2) to create and load the LaunchAgent.

agent_plist=~/Library/LaunchAgents/environment.fix-electron-resource-hog-bug.plist;
/bin/cat <<EOT >$agent_plist && launchctl bootstrap gui/${UID} $agent_plist
<!--
 https://gist.github.com/tkafka/e3eb63a5ec448e9be6701bfd1f1b1e58#temporary-workaround
 To Remove:
 launchctl bootout gui/${UID}/${agent_plist:t:r} && rm ${agent_plist}
-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>${agent_plist:t:r}</string>
        <key>ProgramArguments</key>
        <array>
                <string>/bin/launchctl</string>
                <string>setenv</string>
                <string>CHROME_HEADLESS</string>
                <string>1</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
</dict>
</plist>
EOT

Uses zsh's wonderful variable substitution syntax so you can rename the plist/job if desired.

@tkafka
Copy link
Author

tkafka commented Oct 1, 2025

@avarayr This is excellent, thanks!

I asked Claude to make it look for Electron like the original script (which fixes the Signal detection - it has Electron in Contents/Frameworks/Electron Framework.framework/Electron Framework instead of Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework):

mdfind "kind:app" 2>/dev/null | while read app; do
  electronFiles=$(find "$app" -name "Electron Framework" -type f 2>/dev/null)
  
  if [[ -n "$electronFiles" ]]; then
    appName=$(basename "$app")
    
    while IFS= read -r filename; do
      if [[ -f "$filename" ]]; then
        ev=$(rg -a -m1 -o -r '$1' 'Chrome/.*Electron/([0-9]+(\.[0-9]+){1,3})' -- "$filename" 2>/dev/null)
        [ -z "$ev" ] && ev=$(rg -a -m1 -o -r '$1' 'Electron/([0-9]+(\.[0-9]+){1,3})' -- "$filename" 2>/dev/null)
        
        relativePath=$(echo "$filename" | sed "s|$app/||")
        
        if rg -a -q -F "_cornerMask" -- "$filename" 2>/dev/null; then
          echo -e "$appName \033[2m(Electron ${ev:-unknown}) - $relativePath\033[0m"
        else
          echo -e "$appName \033[2m(Electron ${ev:-unknown}) - $relativePath\033[0m"
        fi
        break
      fi
    done <<< "$electronFiles"
  fi
done

This merge is best of both worlds, I added it above - thanks!

@tkafka
Copy link
Author

tkafka commented Oct 3, 2025

Ladies and gentlemen, we have a winner!

✅ Signal.app (Electron 38.2.0) - Contents/Frameworks/Electron Framework.framework/Electron Framework

Who's next?

@avarayr
Copy link

avarayr commented Oct 3, 2025

Latest beta of GitHub Desktop is also updated.
PR: desktop/desktop#21060

So is VSCode insiders - coming to stable VSCode soon
microsoft/vscode#269279

@avarayr
Copy link

avarayr commented Oct 3, 2025

@bpresles You're missing ripgrep, brew install ripgrep

@bpresles
Copy link

bpresles commented Oct 3, 2025

@bpresles You're missing ripgrep, brew install ripgrep

Yes I figured it out... And it didn't fail because the @tkafka version ("Claude powered") has "2>/dev/null" on the rg commands, making it ignoring any failure...

Here a version checking for the installation of ripgrep:

#! /bin/bash
# Directly detect Electron apps using the _cornerMask override - thanks avarayr!
# Needs ripgrep - brew install ripgrep

if [[ ! -f $(which rg) ]]; then
  echo -e "❌ this script requires ripgrep, you can install it with: brew install ripgrep"
  exit 1
fi

mdfind "kind:app" 2> /dev/null | while read app; do
  electronFiles=$(find "$app" -name "Electron Framework" -type f 2> /dev/null)

  if [[ -n "$electronFiles" ]]; then
    appName=$(basename "$app")

    while IFS= read -r filename; do
      if [[ -f "$filename" ]]; then
        ev=$(rg -a -m1 -o -r '$1' 'Chrome/.*Electron/([0-9]+(\.[0-9]+){1,3})' -- "$filename" 2> /dev/null)
        [ -z "$ev" ] && ev=$(rg -a -m1 -o -r '$1' 'Electron/([0-9]+(\.[0-9]+){1,3})' -- "$filename" 2> /dev/null)

        relativePath=$(echo "$filename" | sed "s|$app/||")

        if rg -a -q -F "_cornerMask" -- "$filename" 2> /dev/null; then
          echo -e "$appName \033[2m(Electron ${ev:-unknown}) - $relativePath\033[0m"
        else
          echo -e "$appName \033[2m(Electron ${ev:-unknown}) - $relativePath\033[0m"
        fi
        break
      fi
    done <<< "$electronFiles"
  fi
done

@mrleblanc101
Copy link

mrleblanc101 commented Oct 3, 2025

Ladies and gentlemen, we have a winner!

✅ Signal.app (Electron 38.2.0) - Contents/Frameworks/Electron Framework.framework/Electron Framework

Who's next?

✅ Visual Studio Code - Insiders.app: Electron 37.6.0 (Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework)

@mrleblanc101
Copy link

The "kind:" mdfind keyword in the script didn't work on my Dutch system install. I found that it only works when the system language is English. As an alternative, you can use mdfind "kMDItemFSName == '*.app'".

Same.
detect-electron-apps-with-macos-lag.sh works, but not detect-electron-apps-using-cornermask.sh
Would be nice to update the script.

@mrleblanc101
Copy link

mrleblanc101 commented Oct 3, 2025

Also, not sure why, but one of the script has echo, the other echo -e which doesn't seem to work on my machine (I'm no bash expert), so the output is prefixed with -e
Capture d’écran, le 2025-10-03 à 11 51 36

@tkafka
Copy link
Author

tkafka commented Oct 3, 2025

@bpresles @mrleblanc101 Thank you both!

  1. I added a check for ripgrep, and a same detection for both scripts, thanks!
  2. echo -e is no longer necessary (it enabled escape sequences for coloring the text; I didn't realize some shells might not have it).

@avarayr
Copy link

avarayr commented Oct 3, 2025

https://avarayr.github.io/shamelectron/

made a website that tracks this bug and automatically updates every day.

feel free to make a PR with your favorite app to track — see example https://github.com/avarayr/shamelectron/tree/main/lib/apps

@MadMacMad
Copy link

MadMacMad commented Oct 4, 2025

i've had some trouble with all those scripts.... non worked ...
so changed the script to make it work...

#!/usr/bin/env bash
set -euo pipefail

if ! command -v rg >/dev/null 2>&1; then
  echo "❌ requires ripgrep (brew install ripgrep)"
  exit 1
fi

scan_dirs=(/Applications "$HOME/Applications" /System/Applications)

find "${scan_dirs[@]}" \
  -path "*/Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework" \
  -type f -print0 2>/dev/null |
while IFS= read -r -d '' filename; do
  app="${filename%%.app/*}.app"
  appName="$(basename "$app")"

  ev="$(rg -a -m1 -o -r '$1' 'Chrome/.*Electron/([0-9]+(\.[0-9]+){1,3})' -- "$filename" 2>/dev/null || true)"
  [[ -z "$ev" ]] && ev="$(rg -a -m1 -o -r '$1' 'Electron/([0-9]+(\.[0-9]+){1,3})' -- "$filename" 2>/dev/null || true)"

  relativePath="${filename#"$app/"}"

  if rg -a -q -F "_cornerMask" -- "$filename" 2>/dev/null; then
    echo -e "❌ $appName \033[2m(Electron ${ev:-unknown}) - $relativePath\033[0m"
  else
    echo -e "✅ $appName \033[2m(Electron ${ev:-unknown}) - $relativePath\033[0m"
  fi
done

chmod +x ~/Desktop/detect-electron-fallback.sh
bash ~/Desktop/detect-electron-fallback.sh

@devnoname120
Copy link

avarayr.github.io/shamelectron

made a website that tracks this bug and automatically updates every day.

feel free to make a PR with your favorite app to track — see example github.com/avarayr/shamelectron/tree/main/lib/apps

@avarayr Very cool, thanks for sharing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment