macOS Power Assertions: Preventing Sleep Without Hiding the Reason
Choose the right macOS IOPM assertion, bound its lifetime, release it reliably, and diagnose which process is keeping a Mac awake.
macOS power assertions let an application request that the power-management system defer selected idle behavior while work is in progress. A backup may need the system awake but not the display; a long export may need a different assertion from a presentation that must keep the screen visible. The type matters: “keep the Mac awake” is not one universal switch.
Assertions are requests to the operating system, not a contract that overrides every power condition. Apple documents that low-power or thermal emergencies can still cause sleep; the idle-system assertion also does not prevent sleep requested by a lid close, the Apple menu, low battery, or other reasons. Applications should therefore explain the reason, choose the narrowest assertion that protects the operation, and release it as soon as the operation ends.
Pick the behavior, not a vague “awake” flag
The IOKit power-management API distinguishes preventing idle system sleep from preventing idle display sleep. A background transfer usually should not keep a bright display on. An interactive presentation may need both the system and display to remain active. Prefer the specific assertion type that corresponds to the work rather than holding the broadest possible request.
IOPMAssertionCreateWithDescription() records a type and diagnostic metadata and returns an assertion identifier. The caller can supply a timeout; zero means no timeout. If a timeout is supplied without a timeout action, the documented default is to turn the assertion off when the timer expires. That is a safety bound, not a substitute for releasing the returned assertion reference. Pair every successful creation with IOPMAssertionRelease() on normal completion, cancellation, error, and application shutdown. A scoped wrapper in Objective-C++ or a defer-style cleanup pattern in Swift makes that pairing visible.
An abbreviated C example illustrates the ownership model:
#include <IOKit/pwr_mgt/IOPMLib.h>
static IOReturn perform_bounded_export(void) {
/* Replace with the real operation and return its IOReturn status. */
return kIOReturnSuccess;
}
IOReturn export_while_awake(void) {
IOPMAssertionID assertionID = kIOPMNullAssertionID;
IOReturn status = IOPMAssertionCreateWithDescription(
kIOPMAssertionTypePreventUserIdleSystemSleep,
CFSTR("com.example.export"),
CFSTR("Writing the final export data"),
NULL,
NULL,
1800.0, /* A 30-minute fail-safe bound. */
NULL,
&assertionID);
if (status != kIOReturnSuccess) return status;
IOReturn workStatus = perform_bounded_export();
IOReturn releaseStatus = IOPMAssertionRelease(assertionID);
return workStatus != kIOReturnSuccess ? workStatus : releaseStatus;
}
The example deliberately requests system idle-sleep prevention but not display-sleep prevention. It checks assertion creation before running the operation, then releases the returned ID even when the operation reports an error. In a real asynchronous exporter, put the ID in an owner whose completion, cancellation, and teardown paths all converge on one idempotent release method; do not return from an early-cancellation branch before cleanup. Keep the timeout as a fail-safe for a hung or lost completion, not as the normal release mechanism.
Make assertions observable
Give each assertion a stable name and a specific reason rather than generic text such as “awake.” Power users and administrators can inspect active requests with pmset -g assertions; correlate that snapshot with the system’s power-management event timeline when investigating sleep and wake behavior. On a managed fleet, capture the assertion owner, type, level, name, and process lifetime before changing power settings or blaming a hardware sleep failure. The IOKit API also provides functions to copy assertions grouped by process or retrieve assertion status when an application needs structured diagnostics.
An assertion is also a useful diagnostic clue. If a Mac never idles, inspect which process owns the active assertion, when it began, and whether its owner still has work. Check for mismatched creation and release paths, retries that create multiple assertions, helper processes that outlive their UI, and operations that never receive a completion callback. Distinguish an assertion from a sleep-preventing process setting or an explicit scheduled wake event; changing a pmset preference is not a repair for a leaked per-operation assertion.
For command-line tasks, Apple’s caffeinate utility offers a user-facing way to hold selected power behavior during a command. It is often better than adding permanent power-setting changes to a script. A command wrapper naturally gives the hold request a process lifetime and can end it when the command exits.
Avoid power-policy side effects
Do not keep the system awake because a timer is running or because a window happens to exist. Define the operation that needs protection, its expected maximum duration, and what happens if the Mac sleeps anyway. A resumable download can checkpoint state and continue after wake; a one-shot write to removable media may need a more conservative completion protocol. Avoid retaining the assertion for a subsequent UI refresh, notification, or cleanup step that does not depend on the machine staying awake.
Do not use an assertion as a substitute for durable data handling. A crash, force-quit, thermal event, battery depletion, or system update can interrupt work regardless. Write checkpoints atomically, flush data when the format requires it, and make retries idempotent. The power assertion reduces one class of interruption; the application still owns correctness.
Finally, test on battery and on AC power, with the display allowed to sleep, with the application canceled, and with the owner process terminated. Verify that the assertion is no longer active after completion and that a deliberately failed operation does not leave an inhibitor behind. Test an expired timeout as well as the normal path, and verify that your explicit release still occurs after a timeout. A correct power request should be narrow, visible, and temporary.
Related:
- Understanding launchd: macOS’s Init System and Service Manager
- Fixing macOS Memory Pressure and ‘Out of Application Memory’ Warnings
Sources: