xbar not working on Mac: what to check
A plugin that runs perfectly in Terminal shows nothing in the menu bar. Or the file is in the right folder and no menu item appears at all. Both are common, both have a small number of causes, and almost none of them are bugs in the app itself. They are places where the contract between the script and the app has been broken.
What follows is an ordered set of checks, arranged so the cheapest ones come first. The specifications quoted below were checked against the project's public documentation on 10 September 2026.
Identify which of the three failures you have
Working out which layer failed saves most of the time.
No menu item appears at all. The file is not being recognised as a plugin. Look at the file name and the folder, not the code.
The item appears but is empty or shows an error. The file was recognised and execution was attempted. Look at the executable bit, the shebang, and PATH.
The item shows stale values. Execution works. Look at the refresh interval and the variable file.
Mixing these up leads to hours spent debugging a script that was never executed. Decide which one applies, then read the matching section.
File naming and folder location
The refresh interval is not a setting inside the app. It is part of the file name, and a name that does not match the pattern will not behave.
The refresh time is in the filename of the plugin, following this format: {name}.{time}.{ext} Source: github.com
Three parts joined by dots. The documented interval modifiers are seconds as 10s, minutes as 1m, hours as 2h, and days as 1d, with date.1m.sh given as an example of a plugin refreshing every minute. A name with only one dot, or with an interval that is not a number followed by a modifier, is the usual reason nothing appears.
The folder matters just as much. Plugins live in the plugins directory inside the app's own folder under Application Support in the user Library. The project's documentation adds a note for anyone arriving from the predecessor app: move the plugins into the new folder to install them. Files left in the old location are simply not seen.
One thing that surprises people: renaming a plugin to change its interval also loses its position in the menu bar, since order is tracked per file. Get the interval right early, then arrange.
The executable bit and the shebang
This is the first suspect when an item appears but stays blank.
The documentation is explicit about permissions and asks that the plugin be made executable with chmod +x. Files created in a text editor, or extracted from an archive, frequently arrive without that bit set. The app runs the file directly, so without it nothing happens and there is no obvious error to read.
The shebang is the second half of the same problem. The documented recommendation is the env form, for example #!/usr/bin/env bash at the top of a shell script. Several language specific caveats are published alongside it and are worth knowing if the plugin is not a shell script:
| Language | Documented caveat |
|---|---|
| Python 3 | The shebang has to be the env python3 form to output unicode correctly |
| Node | The shebang needs the path to the node executable, and process.stdout.write does not produce the expected output |
| Deno | The shebang uses the env -S -P form with an explicit path list |
| Go, interpreted | A specific shebang line is required and go must be on PATH |
| Compiled Go or Swift | A file extension is still required on the compiled binary |
If the plugin is a compiled program rather than a script, remember it still needs a name matching the three part pattern, extension included.
Works in Terminal, blank in the menu bar
This is the most reported symptom and the least obvious cause. The script produces correct output when run manually, and produces nothing when the app runs it. The answer is nearly always PATH.
Terminal starts a login shell and reads your shell configuration, so anything installed by a package manager is on PATH by the time you type a command. An app launched from Finder or at login does not read that configuration. Scripts it starts inherit a minimal PATH, and any command installed outside the system directories is not found. The script fails at the first such command, produces no output, and the menu item is blank.
The documentation acknowledges this directly and suggests adding to PATH from inside the plugin script itself, showing an example that prepends the common directories. One detail deserves attention: on Apple silicon the default package manager prefix is a different directory from the one in that example, and the documentation's Deno shebang example includes that newer path. Check where the command actually lives on your machine before copying either example, because the two are not interchangeable.
Two fixes work. Add the required directories to PATH at the top of the script, or call commands by absolute path. The second is more verbose and survives environment changes better.
The same reasoning applies to environment variables. Credentials or tokens exported in your shell profile do not reach a script launched by the app. Use the variables mechanism described below rather than assuming the environment carries over.
Output that gets swallowed by formatting
The script runs, the output looks right in Terminal, and lines are still missing from the menu. At this point the cause is parsing.
The separator. A line consisting only of --- divides the output. Everything below it appears in the dropdown and never in the menu bar. A line meant for the menu bar placed below the separator will not be visible there.
The pipe character. Everything after a pipe on a line is read as parameters for that item: colour, font, size, link target, keyboard shortcut, truncation length, whether whitespace is trimmed, whether the item is clickable. Output containing an incidental pipe character, common in text pulled from another command, loses everything after it.
Multiple top lines. More than one line above the separator produces a menu bar title that cycles through them in turn, which reads as flickering if it was not intended.
ANSI codes. Parsing of ANSI colour codes is on by default, so raw escape sequences from another tool are consumed rather than printed. There is a parameter to turn that off when the codes should be shown literally. There is also a length parameter that truncates a title and adds a tooltip carrying the full string, which is the correct fix for a menu item that keeps changing width.
Stale values and variables that do not apply
If the item renders but the value never changes, the problem is the refresh path.
The interval lives in the file name, so changing how often a plugin runs means renaming the file. Looking for an interval field in the settings window is time wasted.
Variables are the second half. A plugin declares its inputs in metadata tags, using types documented as string, number, boolean, and select, and those inputs become environment variables at runtime. The values are not stored in the app's preferences. They sit in a JSON file alongside the plugin, named after it, and the documentation states that these files can be modified programmatically. The published example shows a variables file holding a file path and a line count for a tailing plugin.
Editing that file does not update the display on its own. The app exposes control addresses using its own URL scheme, with documented actions to open a plugin, refresh a single plugin, and refresh all plugins. A script that writes new values should call the refresh action afterwards.
There is also an app level configuration file under the same Application Support folder, which does not exist until created. It carries an autoupdate flag and a terminal template. Changes take effect at next launch, and deleting the file and restarting returns the app to defaults, which is a useful way to rule out configuration as a cause.
Two experiments that isolate the cause in a minute
Rather than reasoning about which of the above applies, two quick tests will point straight at the layer that failed.
Test the app with a plugin that cannot fail. Create a file named test.10s.sh in the plugins folder containing a shebang line and a single call to date, make it executable, and refresh. The documentation gives exactly this as the minimal one line plugin. If it appears, the app, the folder, and the naming rules are all fine, and the problem belongs to the other script. If it does not appear, stop looking at the other script entirely, because nothing is being picked up.
Run the failing script the way the app runs it. Executing it in your normal Terminal session proves very little, since that session carries a full PATH and your exported variables. Running it through env -i strips the environment down to close to what a launched app provides. A script that succeeds in one and fails in the other has an environment dependency, and the failure message from the stripped run usually names the missing command outright.
Error output is worth capturing while doing this. Plugins are expected to send errors to standard error rather than standard output, so a script that fails part way can still print a partial menu, which looks like a formatting problem when it is actually a crash. Redirecting standard error to a file during a manual run makes the difference obvious.
When none of that helps
Check the version before reading bug reports. The newest published release and the version distributed by Homebrew are not currently the same, so two people can be running different builds while both believe they are current. The stated system requirement is macOS Catalina or newer.
Then check permissions at the operating system level. A script that reads Documents or Desktop needs the corresponding privacy approval, and one that controls another application needs automation approval. Those prompts appear once. If they were dismissed, later runs fail silently with no output, which looks exactly like the PATH problem. System Settings, Privacy and Security is where those decisions are reviewed.
What to change first
Work top down: file name, then executable bit and shebang, then PATH, then output formatting. Once the plugins behave, deal with the width they consume, because three plugins plus the usual sync and backup icons fill a menu bar quickly, and a MacBook notch removes more of it than people expect. Shorten the output first by moving lines below the separator, then hide the icons that do not need to be visible: how it compares covers the ways to do that, and Koffret is one of them.
Frequently asked questions
My plugin file is in the folder but no menu item shows up. Why?
Almost always the file name. The pattern is name, interval, extension joined by dots, with the interval written as a number plus a modifier such as 10s, 1m, 2h, or 1d. Also confirm the file is in the plugins directory inside the app's folder under Application Support, not in the folder used by the predecessor app.
The script works in Terminal but the menu item is blank. What is different?
PATH. Terminal reads your shell configuration, while an app launched from Finder or at login does not, so commands installed by a package manager are not found. Add the needed directories to PATH at the top of the script, or call the commands by absolute path.
Some of my output lines never appear in the menu bar. Where do they go?
Check for a --- line. Everything below it appears only in the dropdown. Also check for pipe characters in your text, because everything after a pipe is parsed as parameters for that menu item rather than displayed.
I changed a plugin variable and nothing happened. What am I missing?
Variable values live in a JSON file next to the plugin, not in app preferences, and editing that file does not trigger a redraw. Use the app's URL scheme to refresh that plugin or all plugins after the change. To change how often a plugin runs, rename the file, since the interval is part of the name.