My macOS app is getting closed by the system

Hi,

I've been trying to resolve an issue that my users are facing for about one year, but I haven't been able to so far. That's why I'm turning to you all for some ideas.

Some of my users have noticed that my app suddenly exits. It runs in the background as a menu bar app, so when they go to use it, they realize it's no longer running.

I've checked Crashlytics and asked users to check their Console app for crash reports, but there are none. The conclusion so far is that it's not a crash, but a silent termination.

I haven't experienced this on my own machine, which makes it incredibly difficult to debug or identify the cause.

Recently, I thought I'd pinned down the problem. My app was declaring:

	<key>NSSupportsSuddenTermination</key>
	<true/>

Based on the documentation, this is intended to quickly terminate the app during logout or system shutdown, but I read it can also be triggered when the system needs resources. It seemed like the perfect root cause. However, even after turning it off, one of my users is still experiencing the problem. I'm officially running out of ideas.

Does anyone have suggestions on what else I should check?

My app currently declares:

	<key>LSUIElement</key>
	<true/>
	<key>NSSupportsAutomaticTermination</key>
	<false/>
	<key>NSSupportsSuddenTermination</key>
	<false/>
Answered by DTS Engineer in 887802022

Based on the documentation, this is intended to quickly terminate the app during logout or system shutdown.

I'm not sure why we wrote it that way, but I believe that description is somewhat misleading. What "sudden termination" actually does is change how an app is expected to handle quit. The standard quit "flow" is that an app receives kAEQuitApplication, which then lets in opt in/out of exiting through applicationShouldTerminate().

Sudden termination reverses that approach. When sudden termination is enabled, the system "quits" your app by directly terminating it without any notice or warning. Your app is then responsible for telling the system NOT to do that by using disableSuddenTermination()/enableSuddenTermination(). You can read more about it here, but my guess is that it's probably not a factor. That's primarily because it's hard to send kAEQuitApplication to an FBA (Faceless Background App) unless you really dig into the system, but also because I believe we start with sudden termination disabled, so your app need to enable it once before it actually comes into effect.

Next, a quick comment here:

I read it can also be triggered when the system needs resources.

Sort of but not really. Sudden termination itself is a direct replacement for "Quit..." and wouldn't be triggering "on its own". The system itself will terminate processes under high enough memory load, however, that process is fairly obvious (for example, the warning message the system posts telling you to quit apps) and, more to the point, an FBA's resource usage is typically low enough that they tend to be ignored.

I've checked Crashlytics and asked users to check their Console app for crash reports, but there are none. The conclusion so far is that it's not a crash, but a silent termination.

If you're in direct communication with a user, then the best thing you can get is a sysdiagnose that was triggered shortly after the problem occurred (the exact time doesn't matter very much, as long as the machine hasn't been rebooted and it's not "hours" later). Interpreting the console log can be difficult, but it will tell you what happened if you look hard enough.

Having said that...

I've been trying to resolve an issue that my users are facing for about one year, but I haven't been able to so far. That's why I'm turning to you all for some ideas.

...my own recommendation would be to solve the problem by having the system relaunch your app. If you're using SMAppService and you’re configured as an agent, then you can use the "KeepAlive" key in your launchd plist to have the system relaunch your FBA anytime it exists for any reason. See "man lauchd.plist" for the full details. On the other hand, if you're still configured as a login item... well, I would fix that and convert to an agent. Anticipating your questions:

  • Is there some advantage to being a login item vs. an agent?

No. Technically I believe they show up in different places inside the Settings.app UI, but there isn't any fundamental difference between how the two types run or what they can do.

  • Can you think of ANY reason to use a Login Item over a LaunchAgent?

No, not really?

The enablement flow is a bit different for a LaunchAgent, but that's a one-time operation and LaunchAgents are FAR more powerful than Login Items. LaunchAgents can install for "all" users (not just the current user) which is different than LoginItems, but that can be controlled by the user and is what most apps would prefer anyway.

  • Are you saying that macOS has two entirely different mechanisms that basically do the same thing?

Yes.

  • Why?

History.

In the beginning, launchd did not exist at all, and the system had two different problems it needed to solve:

  1. Classic MacOS had a way to launch apps when the user logged in, and users liked it, so MacOS X had to do it too. So "Login Items" were created.

  2. The system needed some way to manage all of the daemons it needed to launch, so "StartupItems" were created. They were okay but not great[1], so in MacOS 10.4, launchd was created, which is when the concept of "Daemons and Agents" was introduced.

Up until macOS 13 and SMAppService, the key difference between those two approaches was that agents were configured via a plist file, and the plist file had to embed the specific path that plist would manage, while "Login Items" were "app" targets which the system would try to track down if/when the app bundled moved. LaunchAgents were always more powerful/configurable than "Login Items," but the hard-coded path limited their usefulness, as did the lack of API support for installing them. However, SMAppService removes that issue.

  • Why haven't Login Items been removed entirely?

Mostly because it would be more trouble than it was worth. The original purpose (giving the user a way to pick the apps they want to launch at login) still exists, so we still need to implement that functionality. Under the hood, they're all being managed by the same infrastructure, so supporting them isn't really any more “work," and removing them would mean we'd have to force all developers to the "new" (but better...) agent architecture. All of that would be far more trouble than it was worth.

[1] In my experience, the more involved you were with boot process in general and StartupItems in particular, the lower your opinion of them was.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Based on the documentation, this is intended to quickly terminate the app during logout or system shutdown.

I'm not sure why we wrote it that way, but I believe that description is somewhat misleading. What "sudden termination" actually does is change how an app is expected to handle quit. The standard quit "flow" is that an app receives kAEQuitApplication, which then lets in opt in/out of exiting through applicationShouldTerminate().

Sudden termination reverses that approach. When sudden termination is enabled, the system "quits" your app by directly terminating it without any notice or warning. Your app is then responsible for telling the system NOT to do that by using disableSuddenTermination()/enableSuddenTermination(). You can read more about it here, but my guess is that it's probably not a factor. That's primarily because it's hard to send kAEQuitApplication to an FBA (Faceless Background App) unless you really dig into the system, but also because I believe we start with sudden termination disabled, so your app need to enable it once before it actually comes into effect.

Next, a quick comment here:

I read it can also be triggered when the system needs resources.

Sort of but not really. Sudden termination itself is a direct replacement for "Quit..." and wouldn't be triggering "on its own". The system itself will terminate processes under high enough memory load, however, that process is fairly obvious (for example, the warning message the system posts telling you to quit apps) and, more to the point, an FBA's resource usage is typically low enough that they tend to be ignored.

I've checked Crashlytics and asked users to check their Console app for crash reports, but there are none. The conclusion so far is that it's not a crash, but a silent termination.

If you're in direct communication with a user, then the best thing you can get is a sysdiagnose that was triggered shortly after the problem occurred (the exact time doesn't matter very much, as long as the machine hasn't been rebooted and it's not "hours" later). Interpreting the console log can be difficult, but it will tell you what happened if you look hard enough.

Having said that...

I've been trying to resolve an issue that my users are facing for about one year, but I haven't been able to so far. That's why I'm turning to you all for some ideas.

...my own recommendation would be to solve the problem by having the system relaunch your app. If you're using SMAppService and you’re configured as an agent, then you can use the "KeepAlive" key in your launchd plist to have the system relaunch your FBA anytime it exists for any reason. See "man lauchd.plist" for the full details. On the other hand, if you're still configured as a login item... well, I would fix that and convert to an agent. Anticipating your questions:

  • Is there some advantage to being a login item vs. an agent?

No. Technically I believe they show up in different places inside the Settings.app UI, but there isn't any fundamental difference between how the two types run or what they can do.

  • Can you think of ANY reason to use a Login Item over a LaunchAgent?

No, not really?

The enablement flow is a bit different for a LaunchAgent, but that's a one-time operation and LaunchAgents are FAR more powerful than Login Items. LaunchAgents can install for "all" users (not just the current user) which is different than LoginItems, but that can be controlled by the user and is what most apps would prefer anyway.

  • Are you saying that macOS has two entirely different mechanisms that basically do the same thing?

Yes.

  • Why?

History.

In the beginning, launchd did not exist at all, and the system had two different problems it needed to solve:

  1. Classic MacOS had a way to launch apps when the user logged in, and users liked it, so MacOS X had to do it too. So "Login Items" were created.

  2. The system needed some way to manage all of the daemons it needed to launch, so "StartupItems" were created. They were okay but not great[1], so in MacOS 10.4, launchd was created, which is when the concept of "Daemons and Agents" was introduced.

Up until macOS 13 and SMAppService, the key difference between those two approaches was that agents were configured via a plist file, and the plist file had to embed the specific path that plist would manage, while "Login Items" were "app" targets which the system would try to track down if/when the app bundled moved. LaunchAgents were always more powerful/configurable than "Login Items," but the hard-coded path limited their usefulness, as did the lack of API support for installing them. However, SMAppService removes that issue.

  • Why haven't Login Items been removed entirely?

Mostly because it would be more trouble than it was worth. The original purpose (giving the user a way to pick the apps they want to launch at login) still exists, so we still need to implement that functionality. Under the hood, they're all being managed by the same infrastructure, so supporting them isn't really any more “work," and removing them would mean we'd have to force all developers to the "new" (but better...) agent architecture. All of that would be far more trouble than it was worth.

[1] In my experience, the more involved you were with boot process in general and StartupItems in particular, the lower your opinion of them was.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

My macOS app is getting closed by the system
 
 
Q