Troubleshooting
Problem, cause and fix for the failures Pulsaria can actually produce: failed or skipped fetches, silent schedules, missing numbers, imports and updates.
Almost every problem in Pulsaria comes with an exact message somewhere in the app. This page maps those messages to their cause and the shortest fix. If it concerns an automatic KPI, start with the fetch log — that is where every attempt is written down.
First stop: the Fetch Log
Open Fetch Log in the sidebar (route /logs). Every automatic attempt lands there: scheduled runs, Run now on a single KPI, and Refresh in the Dashboard header — successes, failures and deliberate skips alike. Click a row to expand it: absolute date and time, HTTP status, stdout, stderr and the response body (stored truncated to 4096 characters). The error message itself sits on the row’s second line.
Four things to know before you read it:
- The source filter offers only All sources, Bash and HTTP. Connector runs carry a
CONNECTORbadge but are visible only under All sources. - The status filter is All / Success / Errors. Skipped runs are drawn with the same red cross as real failures and are counted under Errors.
- The log keeps 500 entries, newest first. Beyond that the oldest are dropped for good.
- The expanded row shows no exit code, no effective command and no effective URL. Where those matter, the message carries them —
Exit code 1,HTTP 401.
Full reference: Fetch log.
A connector fetch fails
Connector errors are translated into readable sentences, most with the first 200 characters of the server’s response appended after Detail:.
| Message | Cause | Fix |
|---|---|---|
| Unauthorized (401). The access token was rejected. | Token or key wrong, rotated or revoked at the provider | App Settings → Connections → pencil icon → paste a fresh secret → Run test → Save connection |
| Access denied (403). Make sure the Service Account email is granted access to the property. | The Service Account address is not on the Search Console property or the GA4 property | Add the …iam.gserviceaccount.com address at the provider, then test again |
| API not enabled in the Google Cloud project (403). | The Analytics Data API or Search Console API is off in that Cloud project | Enable it in the Cloud Console, wait a moment, test again |
| Bad request (400). Check parameters (site URL / property ID). | Wrong parameter format | Edit the connection: a URL-prefix property needs a trailing slash, a domain property needs sc-domain:example.com, a GA4 property ID is digits only |
| Rate limit hit (429). Wait a minute and retry. | Too many calls at the provider | Wait. The next scheduled run is the retry |
| Server error (503). Try again later. | The provider is down | Nothing to fix locally |
| Credentials missing from keychain. Please re-authorize the connection. | The Keychain entry is gone — deleted by hand, a fresh Mac, or a connection that arrived via import | App Settings → Connections → pencil icon → enter the key or token again |
| Connection not found (deleted?) | The connection was deleted but the KPI kept | Open the KPI → Edit → Change next to Connection, or delete the KPI |
| Connector KPI references no metric/connection | The KPI was saved without a metric or connection — the form does not check this | Open the KPI → Edit → pick a metric in the Connector Library and a connection |
| JSONPath ”…” returned nothing | The API answered in a shape the metric does not expect | Test the connection in App Settings → Connections; if it also fails there, the account or the parameters are wrong |
Connector KPIs have no approval step, so they are never skipped — a connector row in the log is either a success or a real failure. More in Connectors.
A custom HTTP fetch fails
Your own HTTP KPIs do not get the friendly mapping above.
| Message | Cause | Fix |
|---|---|---|
| HTTP 401 / HTTP 404 / HTTP 500 | Non-2xx answer, nothing more | Expand the row and read the body block — the API’s own error text is in there |
| Response is not valid JSON | An HTML error page, or a body over 64 KB that was cut off mid-document | Check the URL in a browser; reduce the response with query parameters if the API supports it |
| JSONPath result not numeric | The path missed, or it resolved to something that is not a number | Open the KPI → Edit → Run test. The test run is more specific and says JSONPath "x" did not match |
| Only http:// and https:// URLs are supported | A different scheme | Use https:// |
Every HTTP request is a GET, times out after 10 seconds, follows at most 5 redirects and reads at most 64 KB. These limits are fixed and not configurable. See Custom HTTP sources.
A shell command fails
| Message | Cause | Fix |
|---|---|---|
| Exit code 1 | The command ended non-zero | Expand the row and read stderr |
| Output is not a number | stdout was empty or not parseable as a single number | The command must print exactly one number and nothing else — pipe through jq -r or tail -1 |
| Command timed out after 10s | Longer than the hard limit | Make it faster, or write the number to a file with a launchd job and let the KPI only read the file |
| Shell commands are only available on macOS | Not macOS | No fix — shell KPIs are macOS-only |
The command runs through /bin/sh -c with a cleared environment. Only PATH, HOME and LANG are passed through, and nothing from your shell profile — no .zshrc, no aliases, no exported tokens. A command that works in Terminal and fails here usually cannot find a tool: use the absolute path, /opt/homebrew/bin/jq instead of jq. See Shell commands.
A failed fetch never writes a value. The previous number stays, the KPI’s status is untouched, and nothing is retried before the next scheduled slot.
A fetch was skipped
Two messages mean Pulsaria deliberately did not run anything:
Command not approved — please confirm in KPI settingsHTTP config not approved — please confirm in KPI settings
Open the KPI → Edit → Run test → Approve, then save. After a Refresh the summary toast offers an Approve shortcut that jumps straight into the right dialog. Saving an unapproved auto-KPI also warns you at the time: Saved but not approved — The scheduler will skip this KPI until you approve it.
A command needs re-approval
These two are hard errors, not skips:
Command changed — please re-approveHTTP config changed — please re-approve
Pulsaria stores a fingerprint of the exact configuration you approved and refuses to run anything else. The fingerprint is dropped in three situations:
- You edited the command, the URL, a header or the JSONPath.
- You duplicated the KPI — the toast says “The copy needs re-approval before it fetches automatically.”
- You imported the KPI from a JSON export. Approval is stripped from every imported KPI, always.
The fix is the same each time: open the KPI, Run test, Approve, save. Date tokens such as {{firstDayOfMonth}} do not invalidate an approval — the template is what gets fingerprinted, not the resolved dates.
The number is missing or reads “no data”
- A manual KPI with no entry yet shows an em dash and the badge Add today’s value. Nothing is broken — see Entering values.
- A connector wrote
0. When the API returns no finalised rows for the range, or an empty array to sum, Pulsaria records0instead of failing. That is deliberate: the run counts as done and the next one picks up the real number. - An auto-KPI with an empty schedule field is never fetched on its own. It only moves when you press Run now or Refresh.
- A red card reading “KPI could not be loaded” means the KPI’s data could not be read at all. Clicking it opens the edit dialog. Check the quarantine section below.
- On Daily Entry, auto rows always show today’s value, even when the date picker is set to another day. That is a quirk of that screen, not of your data.
The schedule did not fire
Work down this list — the cause is almost always in the first three points.
- Pulsaria was not running. There is no background agent, no menu-bar item and no login helper. Quitting the app stops all scheduling. Turn on App Settings → Launch at Login so it starts with your Mac.
- You are not on macOS. The whole automatic fetch tick returns immediately on any other platform — including HTTP and connector KPIs.
- The app is read-only. An expired trial or a revoked licence pauses the scheduler silently, with no in-app notice at all. See below.
- The slot was already used. The next firing is calculated from the KPI’s most recent logged attempt, whatever its outcome. A manual Run now at 09:05 counts as that day’s
0 9 * * *run. - Missed runs collapse. If the app was closed for three days, a daily KPI fetches once on the next launch, not three times, and the value lands on today’s date.
- Resolution is one minute, in your Mac’s local time zone. There is no time-zone setting for cron, and the next-run preview in the KPI dialog is computed from now, so it does not show a pending catch-up run.
- A hand-edited, unparseable cron expression never fires and says nothing. The KPI form blocks invalid expressions, so this only happens if you edited
kpi-definitions.jsonyourself.
The first tick runs the moment the app window appears, so anything overdue is fetched right after launch. Details in Schedules & refresh.
Values look off by a day
A fetched value is always stored under the local calendar date of the fetch — not under the date the data describes. Four consequences:
- After a catch-up run, a number that belongs to Tuesday sits on Thursday.
- Monthly connector metrics query the current month up to today, so a 09:00 fetch shows the month so far, not yesterday’s close.
- The Awin connector requests its reports with
timezone=UTCanddateType=transaction. Around midnight its day boundary is not yours. - All date tokens resolve to your local calendar day in
YYYY-MM-DD. The single exception is{{now}}, which is a UTC ISO timestamp.
If several values were recorded for the same KPI on the same day, the card and the history table show the newest one. The earlier ones are not lost: they stay in the intraday list on the KPI detail page and in every export.
The daily reminder never appears
- The app must be running at exactly that minute. The reminder has no catch-up; a missed minute is skipped for the day.
- macOS denied notifications. The settings card then shows Notifications were denied by the system. for a few seconds. Allow Pulsaria in System Settings → Notifications, then use Send test notification to confirm.
- “Only if today is still empty” looks at manual KPIs only. With no manual KPIs at all, or one manual value already recorded today, no reminder is sent. Values fetched automatically neither suppress nor trigger it.
- The notification arrives with the title Pulsaria. Look for that, not for the app name plus a suffix.
The data folder is unreachable
The app suddenly looks empty. Open App Settings → Data Directory and read Current Path. If it points somewhere unexpected, press Use Default and then Save to go back to the built-in folder, or pick the correct folder again with the folder button.
A path you typed or pasted does not work. Pulsaria may only write inside its own app-data folder plus folders you picked in the native dialog — that dialog is what grants the permission, and the grant is remembered across restarts. A hand-typed path leads to failed writes and an app that looks empty. Always use the folder button.
The “Use iCloud” button does not produce a usable path. It fills the field with a malformed string pointing at a container that does not exist for this app. Ignore it. For iCloud sync, use the folder button and pick a folder under ~/Library/Mobile Documents/com~apple~CloudDocs/.
Changing the folder moves nothing. Pulsaria simply starts reading and writing in the new place; your existing files stay where they were. Point it back to the old folder to see that data again. Also note that config.json, connections.json and license.json always stay in the app-support folder and never follow this setting — see Where your data lives.
Nothing detects changes another Mac makes. There is no file watcher and no sync indicator. Switch screens or restart the app to pick up remote writes.
A file was quarantined
If a data file exists but cannot be parsed, Pulsaria keeps working with empty data for that file and preserves the damaged bytes before overwriting them. You will find the copy next to the original:
projects.json.corrupt-2026-07-25T09-13-44-812Z
To inspect or restore it: quit Pulsaria, open the folder in Finder, look at the .corrupt-… file in a text editor, and if it is intact, copy it back over the original file name. Then start the app again.
Two limits worth knowing:
- Only the KPI data files get this protection —
projects.json,kpi-definitions.json,alerts.json,fetch-log.json,reminder-settings.jsonand the files inentries/.config.json,connections.jsonandlicense.jsonare overwritten in place with no quarantine copy. A corruptconnections.jsonis read as “no connections” and your connector connections are then gone; you have to set them up again in App Settings → Connections. - iCloud conflict copies such as
projects 2.jsonare ignored. For projects, KPI definitions, alerts and the fetch log the last writer wins. Measurements are one file per entry, so two Macs writing on the same day do not overwrite each other.
Regular exports are the real safety net here: Backup, export & moving Macs.
Import problems
The file is refused. Not a Pulsaria export (expected an object) or Not a Pulsaria export (missing projects / kpiDefinitions / entriesByDate) means the file is not a Pulsaria JSON export. Only JSON exports can be read back — there is no CSV import.
Import JSON is greyed out. Import needs an active trial or a licence; the tooltip reads Requires a license or active trial. Export stays available in every state.
“Nothing new to import — everything in the file already exists.” The file was analysed and contains no new items and no conflicts. Nothing was written, and no dialog opens.
Items that already exist. For each conflicting KPI and connection you choose Keep local or Replace, or Keep all remaining to stop deciding. Projects are the exception: on a conflicting id the local project always wins, with no choice offered.
Some things arrive quietly reduced. Rows that fail validation are dropped without a message, and the count is never displayed. The most common trap: a KPI whose target is not a finite number greater than zero is dropped entirely. Entries are deduplicated by KPI and creation timestamp, so importing the same file twice adds nothing. The fetch log is exported but never imported, and app settings, reminder settings and the licence are not part of an export at all.
After the import, connector KPIs fail. Two expected effects:
- Every imported Bash or HTTP KPI lost its approval and must be approved again.
- Credentials are never in an export. Pulsaria opens the setup wizard once per connection (“Connection 2 of 3 — re-enter credentials”) so you can paste the key or token. If you close that wizard, the connection stays without credentials and its KPIs fail with
Credentials missing from keychain. Fix it later in App Settings → Connections with the pencil icon.
Activation problems
The activation toast is always titled Activation failed: — the useful sentence is underneath it.
| Message | What to do |
|---|---|
| Couldn’t reach the license server. | Check the connection, a VPN or a firewall, then press Activate again |
| License key not found. | Re-copy the key from the order confirmation email; a truncated paste looks exactly like this |
| This license is already active on the maximum number of devices. | Free a slot: Deactivate this device on the old Mac, or use the Polar customer portal |
| This license has been revoked. | The key was cancelled on the seller’s side. Contact support |
| Invalid license key format. | The text does not look like a licence key at all |
Activation, re-checks and deactivation all need internet — there is no offline activation code. An amber Please reconnect — license not verified recently is a nudge only: the app stays fully writable, and going online plus Re-check clears it. Everything about keys and devices: Trial & licence.
Read-only after the trial
When the trial expires or a licence is revoked, Pulsaria keeps every screen and every number visible and blocks only changes. You will notice it as:
- Greyed-out buttons with the tooltip Requires a license or active trial, and the toast Read-only mode — Pulsaria is in read-only mode. Activate a license to make changes.
- Auto-KPIs that stop updating, with no notice anywhere. The scheduler and the daily auto-export both pause quietly.
- Run now on a single KPI, which is not greyed out but fails with Auto-KPI failed and the raw reason
Trial expiredorLicense revoked.
Export JSON and Export CSV keep working, so your data is never locked in. Activate a licence in App Settings → License to end the state.
The daily auto-export is not writing files
The status line in App Settings → Daily auto-export is the only signal — there is no notification. Last export failed — check the location means the last attempt threw; hover the line for the message.
- The card’s own hint says it: the scheduled export can only be relied on inside the app’s data folder. A custom folder elsewhere may work only while the app is open.
- After a failure, Pulsaria does not retry every minute. It tries again after a restart, or immediately when you press Export now.
- It fires once per calendar day at or after the configured time, and it needs an active trial or licence.
- Nothing is pruned: one JSON and one CSV per day accumulate forever. Same-day files are overwritten.
Updates are not installing
- Update check failed — the check could not reach Pulsaria’s update endpoint. A VPN, a firewall or a captive network is the usual reason. Press Check now again later.
- You’re on the latest version is also what the card shows before the first check has returned and after a failed one. It is not proof that a check succeeded — the line underneath, Last checked at … or Not checked yet, is.
- Update install failed — the download or the signed install did not complete. Try again; if it keeps failing, download the current version from the Pulsaria website and install it over the existing app. Your data is untouched by a reinstall.
- Nothing is ever downloaded or installed without a click on Download & install. The app restarts itself when the install finishes.
- There are no update channels, no “skip this version” and no rollback. The Updates card exists on desktop only.
Still stuck: contacting support
Open App Settings → Feedback → Send feedback. Your mail app opens with a message to admin@one-pixel-ahead.com, subject “Pulsaria feedback”, and exactly three diagnostic lines appended: app version, platform and interface language. Nothing is sent automatically and nothing else is attached — you can edit or delete any of it before sending.
Nothing from your data goes along, so it helps to add by hand:
- What you did, what you expected, what happened instead.
- The exact message from the fetch log row, copied as text. Never paste an API key, a token or a Service Account file into an email.
- Whether the same thing happens after Run now, and whether the connection’s Test in App Settings → Connections succeeds.
If the failure is a crash rather than an error message, turning on App Settings → Crash Reporting before reproducing it sends the stack trace — never KPI names, values or file paths. See Privacy: what leaves your Mac.
For a data source Pulsaria does not cover yet, use Request a connector at the bottom left of the Connector Library. That also just composes an email; nothing is transmitted in the background.