Vee documentation
Troubleshooting
Common issues and how to fix them. If none of these help, open an issue at github.com/navbytes/vee.
A plugin doesn't appear in the menu bar
Work through these in order:
- Is it executable? Vee runs plugins as programs. Mark the file executable:
chmod +x ~/Library/Application\ Support/Vee/plugins/mine.5s.sh(If a file is not executable, Vee still tries its shebang interpreter and falls back to bash — but
chmod +xis the reliable path.) - Is it in the plugins folder? The default is
~/Library/Application Support/Vee/plugins. Confirm the folder Vee is using in the Plugin Manager (Choose Folder), and make sure the plugin is directly inside it. - Is the filename valid? The interval token must sit right before the extension:
cpu.5s.sh, notcpu.sh.5s. A file likecpu.shwith no interval runs on demand, not on a timer. - Is it enabled? Check the Plugin Manager — a disabled plugin stays on disk but is not run or shown.
- Force a scan. Use Refresh all from the Vee menu, or toggle the plugin off and on.
"unidentified developer" / Gatekeeper blocks the app
Vee is notarized, so a normal double-click should work. If macOS still blocks it:
- Right-click (or Control-click)
Vee.appin/Applicationsand choose Open, then confirm the dialog. macOS remembers the choice after the first time. - Alternatively, open System Settings → Privacy & Security, scroll to the message about Vee being blocked, and click Open Anyway.
Make sure you downloaded Vee.app from the official GitHub Releases page.
A plugin errors or shows nothing useful
- Run it in a terminal first. A plugin is just a script — run it directly and read the output:
~/Library/Application\ Support/Vee/plugins/mine.5s.shIf it errors there, fix it there. Vee runs the plugin with your environment plus its own injected variables (see authoring).
- Check the first line before
---. Only the text before the first---becomes the menu-bar title. If your title line is empty or errors, the menu-bar item looks blank. - Watch quoting. Line parameters after
|must be quoted when they contain spaces (title="two words"), and quotes inside values escaped (\").
A plugin times out
Vee runs each on-demand plugin with a timeout (30 seconds by default) and kills the process if it overruns. If your plugin does slow work (a slow network call, a heavy computation):
- Make it faster, or cache results between runs (use
SWIFTBAR_PLUGIN_CACHE_PATH/SWIFTBAR_PLUGIN_DATA_PATH). - If it is genuinely long-running and pushes continuous updates, make it a streaming plugin instead (
<swiftbar.type>streamable</swiftbar.type>with~~~separators), which stays running rather than being re-invoked on a timer. See Streaming.
"command not found" / a dependency or interpreter is missing
Vee does not install your plugin's dependencies. If a plugin needs python3, node, jq, gh, etc., that tool must be installed and on the PATH.
- Check the plugin's
<xbar.dependencies>header for what it needs. - Verify the tool exists:
which python3,which jq, and so on. - PATH: Vee resolves your login shell's
PATHat launch (running$SHELL -ilc) and adds the usual Homebrew locations, so tools installed via Homebrew, pyenv, asdf, or nvm are normally found just like in Terminal. If a tool is configured somewhere unusual (or only in a non-login shell rc file) and still isn't found, use its absolute path in the plugin (e.g./opt/homebrew/bin/jq), or setPATHexplicitly at the top of the script. - For a script without a shebang and without the executable bit, Vee falls back to
/bin/bash. Add a proper shebang (#!/usr/bin/env python3) so the right interpreter is used.
Refreshes aren't happening
- Confirm the interval. The filename controls it:
weather.10m.shis every 10 minutes,weather.shis on demand only. - Cron plugins use
<swiftbar.schedule>(5-field cron). Double-check the expression — an invalid field means it never fires. - Manual refresh always works: the plugin's own dropdown has a refresh action if it prints one (
refresh=true), and the Vee menu has Refresh all. - Plugins can trigger refreshes via URL actions (
vee://refreshplugin?name=…).
Permissions (network, files, notifications)
Plugins run with your user privileges, so they generally have the access you do. A few things to know:
- macOS privacy prompts. The first time a plugin (through Vee) touches a protected area — Contacts, Calendar, files in protected folders, etc. — macOS may prompt. Grant access in System Settings → Privacy & Security if you trust the plugin.
- Notifications. For
vee://notifyto show alerts, allow notifications for Vee in System Settings → Notifications. - The
<vee.*>trust declarations are advisory — they describe what a plugin says it does, and Vee never blocks based on them. See the trust model.
Diagnostics reference
Vee's parser is deliberately permissive: bad input degrades to a diagnostic and
a best-effort render, never a crash and never a blank menu. Those diagnostics
are collected per run and shown in the plugin's Debug console (Plugin
Manager → the plugin), and printed by
vee render and vee lint.
Every one of them means something you wrote was ignored or altered, so an empty diagnostics list is the goal.
Line and parameter problems
| Diagnostic | What happened | Fix |
|---|---|---|
unknown parameter '…' | A key= Vee does not recognise. The value is preserved but nothing renders from it. | Check the spelling against the parameter table. |
duplicate parameter '…' | The same key appeared twice on one line. | Remove one; the winner is not something to rely on. |
parameter '…' has no value | A bare key with no =value. | Give it a value, or drop it. |
value for '…' contains a space but isn't quoted | The value was cut short at the space. | Quote it: tooltip="two words". |
stray '|' in title text | A later | was read as the params delimiter and truncated the item. | Escape it as \|, or let an SDK do the escaping. |
alternate item has no preceding item | alternate=true on a line with nothing above it to be the alternate of. | Move it below the item it alternates with. |
paramN given without shell=/bash= | Positional arguments with no command to pass them to. | Add shell=/bash=, or remove the params. |
submenu depth exceeded; truncated | More than 64 levels of -- nesting. | Flatten the menu — this is far past usable. |
submenu depth jumped; clamped to … | A line nested more than one level deeper than its parent (e.g. -- then ------). | Add the intermediate level, or reduce the dashes. |
Images
| Diagnostic | What happened | Fix |
|---|---|---|
image=/templateImage= is not valid base64; dropped | The payload did not decode. | Re-encode; check for stray newlines in the base64. |
image=/templateImage= decodes to over 2097152 bytes; dropped | Over the 2 MB image cap. | Shrink the image — menu icons need very few pixels. |
Charts and gauges
| Diagnostic | What happened | Fix |
|---|---|---|
progress= expects a fraction (0..1) or 'value,max' | The value did not parse. | Use progress=0.72 or progress=23,100. |
pie=/donut=/stackedbar= expects a comma-separated list of non-negative numbers | The list did not parse. | Use pie=45,30,25. |
… values must all be finite and non-negative | A NaN, an infinity, or a negative share. | Clamp the values before printing them. |
… values sum to zero; nothing to chart | Every share was 0. | Skip the chart when there is no data, rather than emitting zeros. |
… has N segments; the last M were folded into 'Other' | More than 8 segments. | Aggregate the tail yourself if you want to control the label. |
chartlabels=/chartcolors= given without pie=/donut=/stackedbar= | Labels or colors with no chart to attach to. | Add the chart param, or remove them. |
chartcolors= has a malformed color; those segments use the default palette | A color name or hex Vee could not read. | Check against the named colors, or use #rrggbb. |
accessory= expects 'leading' or 'trailing' | Any other value. | Use one of the two. |
slider= expects 'min,max,value' with min < max | Malformed or inverted bounds. | e.g. slider=0,100,40. |
URLs
| Diagnostic | What happened | Fix |
|---|---|---|
href= has a missing or unsafe url; dropped | The URL did not parse, or used a blocked scheme. Only http, https, and app deep links open — never file:, javascript:, and the like. | Use a web URL, or run a command with shell= instead. |
abouturl has a missing or unsafe url; dropped | Same gate, on <xbar.abouturl>. | As above. |
Widget cards
Only from a widget-mode run.
| Diagnostic | What happened | Fix |
|---|---|---|
widget output is not a JSON object | VEE_TARGET=widget produced something that is not one JSON object. Vee falls back to scraping the menu text. | Print exactly one JSON object and nothing else. |
unknown widget template "…"; using stat | A template outside the five. | Use stat, gauge, trend, list, or board. |
unknown widget status "…"; ignored | A status outside the three. | Use ok, warning, or error. |
progress is not a finite number; dropped / progress N outside 0...1; clamped | A bad or out-of-range gauge fill. | Clamp to 0…1 before printing. |
trend contained non-finite values; dropped | A NaN/infinity in the series. | Filter the series first. |
href action "…" has a missing or unsafe url; dropped | A card action's URL failed the scheme gate. | Use a web URL or an app deep link. |
Widget layout trees
| Diagnostic | What happened | Fix |
|---|---|---|
unknown layout node type "…" | A type outside the ten. The node renders as nothing. | Check it against the node types. |
layout nested deeper than 8 levels; inner nodes dropped | Past the depth cap. | Flatten the tree. |
layout has more than 64 nodes; extra nodes dropped | Past the node cap. | Send less — a widget tile is small. |
layout text longer than 512 characters; truncated | A very long text node. | Shorten it; it would not have fit anyway. |
layout sparkline longer than 256 points; truncated | Too many series points. | Downsample before printing. |
layout sparkline contained non-finite values; dropped / layout gauge value is not finite; dropped | A NaN/infinity. | Filter or clamp first. |
Run-level
| Diagnostic | What happened | Fix |
|---|---|---|
Output truncated at 8 MB | The plugin printed more than the capture cap. | Almost always accidental — a log or an unbounded loop reaching stdout. |
Still stuck?
- Reveal the plugin in Finder from the Plugin Manager and read its source.
- Run it directly in Terminal to isolate app-vs-script issues.
- File an issue with the plugin's output and your macOS version at github.com/navbytes/vee.