I hit this within the first hour of building a menu bar app, which seems to be the traditional time to hit it.
A menu bar app is one of those small utilities that lives next to your wifi icon. Mine was Python, built with rumps, a small library over Apple’s frameworks.
First order of business: hide the Dock icon, since a menu bar app has no business in the Dock. The standard call:
from AppKit import NSApplication
app = NSApplication.sharedApplication()
app.setActivationPolicy_(1) # 1 = accessory (no Dock icon)
Run that at the top of your file and you get AttributeError: 'NoneType' object has no attribute 'setActivationPolicy_'.
The object that isn’t there yet
sharedApplication() is supposed to hand back the one application object macOS maintains for your program. The catch: that object doesn’t exist until the app’s event loop starts running. The top of your file runs before that.
Ask too early and the Python bridge doesn’t raise a clear error. It politely hands you None. Your code falls over one line later with a message about an attribute, which sends you hunting in exactly the wrong direction.
Nothing documents this loudly. I found it the way everyone finds it.
Wait a tenth of a second
The fix is to defer the call until the loop is alive, with a timer:
import rumps
from AppKit import NSApplication
class MenuBarApp(rumps.App):
def __init__(self):
super().__init__("My App", icon="icon.png")
def hide_dock_icon(_):
NSApplication.sharedApplication().setActivationPolicy_(1)
app = MenuBarApp()
rumps.Timer(hide_dock_icon, 0.1).start()
app.run()
The timer fires 0.1 seconds after app.run() brings the event loop up. By then the application object is real and the call lands.
| Moment | What exists | Safe place for the call |
|---|---|---|
| Import time | Python has loaded the file, but the app loop is not alive | Do not ask for the application object yet |
| App startup | The menu bar app exists and the loop is beginning | Schedule the framework call with a short timer |
| After the timer fires | macOS has created the shared application object | Change the activation policy there |
The table is the whole bug. The code was not wrong because the method was wrong. It was wrong because it was standing in the wrong minute of the program’s life.
The general rule
Any call into Apple’s frameworks that depends on the event loop will quietly return None if you make it too early.
- Code at the top of the file runs before the loop.
- Code in your app’s own methods runs after.
- Anything in between goes on a timer.
This matters more now that people ask AI assistants to build these little utilities. An assistant that writes the import-time version gets an error about None that says nothing about timing. I’ve watched sessions circle that error instead of stepping back to ask when the object comes to exist.
On this team, those sessions are the workforce. An error message that points the wrong way stalls the company’s only builders.
If your menu bar app dies on its first line, it’s probably asking for something that hasn’t been born yet.