TL;DR — The widely-documented fix is wrong on current macOS. ChimeOnNoHardware is
the real off-switch, and it must be set to true, not false. Setting both keys to
false (what every guide from 2015–2022 says) is actually a play-the-chime configuration.
# The fix that works on macOS 26.x:
defaults write com.apple.PowerChime ChimeOnAllHardware -bool false
defaults write com.apple.PowerChime ChimeOnNoHardware -bool true # ← true, not false
killall PowerChime # drop the idle instance; next charger event re-reads prefs# The classic fix, for contrast — does NOT silence the chime anymore:
defaults write com.apple.PowerChime ChimeOnAllHardware -bool false
defaults write com.apple.PowerChime ChimeOnNoHardware -bool false # this KEEPS it onPowerChime is spawn-on-demand: launchd launches a fresh process on each AC-attach power
event, it decides whether to chime, then exits. The decision above runs on every spawn,
including the brief DarkWake spawns that happen when a charger is attached with the lid
closed — so this covers the "charging overnight, lid shut" case too.
If the preference route ever fails, the fallback is to stop launchd from spawning the
process at all:
launchctl disable gui/$(id -u)/com.apple.powerchime # note: launchd label is lowercase
launchctl bootout gui/$(id -u)/com.apple.powerchimeThe process logs its decision on every launch. Trigger a real charger attach (unplug → replug), then:
log show --last 2m --predicate 'process == "PowerChime"' --info \
| grep -iE "disabled|PLAYING CHIME"- Silenced correctly →
PowerChime disabled - ChimeOnNoHardware default - Still chiming →
PLAYING CHIME SOUND
The working command is not new; ChimeOnNoHardware -bool true appears in a handful of
guides. What's missing everywhere is why it works and why the more popular alternative
does not. The internet carries two contradictory one-liners, usually presented with equal
confidence and no explanation:
| Variant | Command | Works on current macOS? |
|---|---|---|
| A | defaults write com.apple.PowerChime ChimeOnNoHardware -bool true |
✅ hits the exit(0) path |
| B | defaults write com.apple.PowerChime ChimeOnAllHardware -bool false |
❌ this is the false/false = chimes cell |
Variant A — the correct one — is documented in, e.g., a gist that is literally just that one line, MakeUseOf, and AppleToolBox.
Variant B — the widely-copied trap — is the "classic" advice, e.g.
this gist disables with ChimeOnAllHardware -bool false.
It is also what you end up with if you read that both keys control the chime and set both
to false: per the truth table above, false/false plays. This is the likely source of
the recurring "it worked, then the chime randomly came back" reports — the config was never
actually in the silencing state to begin with.
The contribution here is not the command; it's the disassembled gate that shows which
variant is correct, and that the popular ChimeOnAllHardware -bool false / both-false
advice is a no-op for silencing.
/System/Library/CoreServices/PowerChime.app/Contents/MacOS/PowerChime
Universal binary (x86_64 + arm64e), a small AppKit agent (LSUIElement). It links
libSMC, SkyLight, BatteryUIKit, IOKit, AudioToolbox/CoreAudio, and carries
private entitlements for playing audio during notification-wake, asserting on lid close,
and DarkWake control — i.e. it is explicitly allowed to chime while the machine is
technically asleep with the lid shut.
strings -a PowerChime | grep -iE "Chime|hardware|com\.apple"Both legacy keys are still present in the binary, alongside the log lines that reveal the control flow:
ChimeOnAllHardware
ChimeOnNoHardware
PowerChime enabled by ChimeOnAllHardware default
PowerChime: chime enabled by hardware: %hhd
PowerChime disabled - ChimeOnNoHardware default: %hhd
Notably, the string com.apple.PowerChime appears nowhere in the binary (only as the
bundle identifier in Info.plist). There is no secret alternate preferences domain — the
keys are read from the process's own NSUserDefaults, whose search list is the
com.apple.PowerChime domain. So the domain everyone writes to was correct all along; only
the value was wrong.
The preference symbols in use:
nm -u PowerChime | grep -iE "Preferences|UserDefaults"
# _CFPreferencesGetAppBooleanValue, _CFPreferencesCopyAppValue, ... (chimeAttach animation prefs)There are two distinct preference groups:
com.apple.chimeAttach.*(lightDisplay,numIterations,iterationDelay) — read viaCFPreferencesGetAppBooleanValue/GetAppIntegerValueinreadChimeAttachPrefs. These control the visual display-flash animation, not whether sound plays. Easy to mistake for the gate; it isn't.ChimeOnAllHardware/ChimeOnNoHardware— read via[[NSUserDefaults standardUserDefaults] boolForKey:…]insidemain. This is the sound gate.
Cross-referencing the key strings back to their use sites lands both reads in one function
(main, 0x100008678 in the arm64e slice).
Resolving the selector references confirms the two reads are
boolForKey:@"ChimeOnAllHardware" and boolForKey:@"ChimeOnNoHardware" on
[NSUserDefaults standardUserDefaults]. The control flow (annotated):
; allHW = [defaults boolForKey:@"ChimeOnAllHardware"] -> w23
; noHW = [defaults boolForKey:@"ChimeOnNoHardware"] -> w21
tbz w23, #0, Lelse ; if (allHW == false) goto Lelse
; log "PowerChime enabled by ChimeOnAllHardware default"
b Lrun ; -> NSApplicationMain (CHIME PLAYS)
Lelse:
; log "PowerChime: chime enabled by hardware: %hhd"
cbnz w21, Ldisabled ; if (noHW == true) goto Ldisabled
; fallthrough:
Lrun:
... b NSApplicationMain ; (CHIME PLAYS)
Ldisabled:
; log "PowerChime disabled - ChimeOnNoHardware default: %hhd"
xpc_set_event_stream_handler("com.apple.notifyd.matching", ...)
exit(0) ; bails before ever playing (NO CHIME)
Equivalent source:
BOOL allHW = [defaults boolForKey:@"ChimeOnAllHardware"];
BOOL noHW = [defaults boolForKey:@"ChimeOnNoHardware"];
if (allHW) {
NSApplicationMain(...); // chimes
} else if (noHW) {
exit(0); // does NOT chime
} else {
NSApplicationMain(...); // chimes
}ChimeOnAllHardware |
ChimeOnNoHardware |
Result |
|---|---|---|
true |
(any) | 🔊 chimes |
false |
false |
🔊 chimes ← the "documented fix" |
false |
true |
🔇 exits before chiming |
The classic advice — set both to false — is the exact cell that keeps the chime on.
That is why the documented fix appears to do nothing: the keys are written correctly, a
fresh process spawns and re-reads them, and the gate still resolves to "play." The only
preference combination that reaches the exit(0) path is ChimeOnNoHardware = true.
You cannot validate this by launching the binary directly. It is a restricted platform
binary, so a manual launch is killed by the kernel with a Launch Constraint Violation
(SIGKILL, CODESIGNING namespace) before main executes — the gate never runs and
nothing is logged. The only end-to-end test is a real charger-attach event via launchd,
observed through the unified log (see "Verifying it worked" above). The static analysis,
however, is unambiguous, and the false/false = chimes result it predicts exactly matches
the observed real-world symptom.