Error Management
The Issues screen lists every unique JavaScript and PHP fault captured from your site. One row is one issue, not one occurrence: errors are grouped by a fingerprint computed from their type and their normalised message, so a broken template that fires ten thousand times is one row with a count, not ten thousand rows.
The list
Navigate to Dashboard → Issues.
Each row shows the message, its severity, the status, how many times it has been seen, how many visits by people it reached (visits judged automated are not counted), and when it was first and last seen.
Those two counts answer different questions and are worth reading together: ten thousand occurrences across four visits is one visitor's browser in a loop, while ten thousand across four thousand visits is something every customer is walking into.
Each column title carries one control. A column of words — a severity, a status — filters: press the funnel icon beside its title and tick what you want to see. A column of numbers or dates sorts: press the arrows beside its title.
| Column | Control |
|---|---|
| Severity | Filter: Notice / Warning / Error / Critical — several at once |
| Status | Filter: New / Investigating / Resolved / Ignored — several at once |
| Count | Sort: most or least frequent |
| Visits | Sort: most or fewest people |
| Last seen, First seen | Sort: newest or oldest |
The issue's type, its source and where in the code it lives are on the issue page. A link that narrows the list by type, by source or to the issues a person met still works, and shows the narrowing as a removable chip.
Above the table, New issues per day shows the last 30 days in your own time zone, with the 30 days before them dashed for comparison.
The cards above it count the period you picked. Issues seen and Critical + error carry bars of how many issues fired on each day — a bar can never add up to its card, because the card counts every issue seen at any point in the period. Investigating and Resolved are how things stand right now, so they have no bars. Days before StorePilot began keeping this daily count (October 2026) are left blank rather than drawn as quiet days.
The search box above the table matches message text. A list opened from a visitor's page arrives narrowed to that visitor, and says so with a removable chip beside the search.
Where in the code
The issue page names which part of a WordPress install the fault came out of:
| Value | What it means |
|---|---|
| Theme | wp-content/themes/… — your parent or child theme. Both read as "Theme": the difference is a directory name, and the question ("is this my own code?") has the same answer for either. |
| Plugins | wp-content/plugins/… or mu-plugins/… |
| WordPress core | wp-includes/… or wp-admin/… — jQuery lives here |
| Elsewhere | A file was named and it is none of the above: a third-party script on a CDN, a bundle a page builder wrote into uploads/, an inline script |
When nothing names a file at all — a cross-origin script the browser refused to describe, a
console.error with no stack, a PHP fault with no path — the list prints nothing there.
Two things are worth knowing about how the value is decided. When the browser (or PHP) names the file the fault happened in, that file decides on its own. When it does not, the stack is read instead, and a theme frame anywhere in it wins over a plugin frame, which wins over a core one — because almost every WordPress stack passes through jQuery on its way out, and taking the innermost frame would file your theme's own bug under "WordPress core".
The label appears once the values have been computed for your site. If you self-host and have
just upgraded, an administrator runs pnpm errors:classify once to label issues that predate the
feature.
Browser notices are listed
Some reports are the browser's own behaviour rather than a fault anybody can fix: a
ResizeObserver loop notice, an iOS autofill internal, an autoplay refusal, a permission the
visitor declined. They are listed with every other issue — narrow them out with the column
filters — and they are born at notice severity, so they never mail anybody, count against
your plan or feed the spike baseline. Nothing is ever deleted. The API and connected AI
clients leave them out unless asked.
Script error. is deliberately not hidden: it looks like noise and it is fixable, and the
issue page explains how.
Faults in StorePilot's own collector are excluded
A report whose stack runs entirely through StorePilot's own script is our defect, not your shop's — there is nothing you could change on your site to fix it. Those reports are kept out of the list, out of the CSV export, out of your plan's usage and out of alerts. There is no control to show them, because there is no action to take: we see them on our side and they are never deleted.
A fault in your code that merely passes through our script on its way out — a request your theme makes failing inside our network wrapper, say — is still your issue and stays in the list.
Which issues you have already looked at
Every issue you have not opened is marked with a dot and a heavier title. Opening it clears the mark — from anywhere, including a link in an alert email.
The mark is yours, not your team's: a colleague reading an issue does not mark it read for you, and it follows your account rather than your browser, so the same issue reads the same way on your laptop and your phone.
Unseen in the toolbar narrows the list to what is left, Mark all as read clears the backlog in one press, and selecting rows offers Mark as read and Mark as unread — the second is how you put something back on your own queue.
New occurrences of an issue you have already read do not mark it unread again. A fault that happens every minute would otherwise be permanently unread, and the mark would stop meaning anything within a day.
Leads and Recordings carry the same mark, and it works the same way on both.
Statuses
| Status | Effect |
|---|---|
| New | Untriaged |
| Investigating | In progress — notifications unchanged |
| Resolved | Fixed. If it happens again it re-opens and notifies |
| Ignored | Acknowledged, not being worked on. Notifications stop |
Muting is separate from status. The bell in a row's Actions column switches that issue's notifications off — or back on — and leaves its status alone, so an issue can be muted and still be Investigating. On the issue's own page the same menu also offers a snooze: off for an hour, a day or a week. A flapping fault that keeps resolving and coming back will page you on every round trip; muting or snoozing it stops the notifications without pretending it is fixed, so it stays in the triage queue where you can see it. Muting needs the member role.
The eye in the same column opens the issue.
Select several rows with the checkboxes to change status or delete in bulk. Changing status in bulk needs the member role; deleting needs admin.
The issue page
It answers, top to bottom, the questions people arrive with.
Impact — across the top of the page: how many people and how many of their visits (automated traffic is not counted there), how many pages and how many occurrences, then one line splitting ALL the visits into human, automated and suspected (the ⓘ beside each says what it means). Under it, Occurrences per day charts the last 30 days so you can see whether it is getting worse; point at a day to read its occurrences and how many visits they came from. These are counts, never percentages of your traffic: errors and pageviews are not collected under identical rules (a site past its plan ceiling keeps reporting errors and stops reporting pageviews), so a ratio between them would sometimes describe your plan rather than your site. The window the figures cover is stated under them.
Carts at risk — the carts this fault could have stopped: a person met it on that cart's own checkout page, on your cart page (WooCommerce reports which page that is from plugin 1.41.0), or while the order was being placed, and the cart was left unpaid. A fault on another page of the visit does not count — that person still reached the checkout — and neither does a notice, browser noise or a visit we judged automated. It covers the last 30 days, the time each occurrence is kept. Still a reason to look, not proof.
Where in the code — the stack, resolved back to your own source if you have uploaded a source
map for that release. Some reports have no stack and never will — a file that failed to load, a
deliberate console.error, a cross-origin script the browser refuses to describe, a fault
reported by your PHP — and the page says which of those it is and what to do, rather than showing
you an empty box.
Happens in N places in the code — shown when one message is thrown from more than one place (the cart drawer and the quick-view modal both failing to read a price, say). Each place has its share of the occurrences, when it was first and last seen, and its own sample stack: open a place to see it, resolved through your source maps if you uploaded them. Up to ten places keep a sample; more are still counted.
Where it happens — the busiest pages, and who met it: browsers, systems, devices, countries, whether the visitor had an account in your shop, and releases, each as a small table. Each figure is the occurrence's own — the browser it happened in, not whatever the visitor used last. Visits is how many of this issue's visits had that value, and This issue is their share of the visits where that detail was recorded, so «Safari 80%» answers whether it is only Safari. Press any value to narrow the occurrence list below to it; Show all brings the rest back.
For browsers, systems, devices and countries, All visits is the same value's share of all your visitors over the last 30 closed days. A ▲ beside the share — More often than usual — marks a value this issue meets at least twice as often as your shop does, over at least five visits, so three Safari visits out of three is not a finding. That comparison counts people only, never automated traffic, and the line under the tables says so. If your plan's limit paused visit collection during those days the comparison is left out, and the page says so — it would compare against an incomplete shop. Accounts and releases have nothing to compare against, so their tables show only the share.
Occurrences and Recordings — when exactly, and the sessions you can watch. Each line says who it happened to, and marks a value this issue rarely sees as rare — the exception is often where a second cause hides. Occurrences recorded before October 2026 carry no such details. A fault your server raised outside any visitor's request — a scheduled job, a REST call — is on the list too, with the page it was raised on, at most 30 lines an hour per issue; the count above it includes every one.
Automatic re-open, and regressions
When an issue is resolved or ignored and the same fingerprint arrives again, it re-opens: the
status goes back to new, the occurrence count restarts at 1 — you are counting this recurrence,
not adding to the one you already closed — and a notification fires.
That transition is also what records a regression. If the incoming report carries a release, the issue remembers which build brought it back, and the list badges it:
Regressed · v1.4.2
The badge is shown only while the issue is open again; re-resolve it and the badge goes. An issue that was already open and simply happened again is not a regression — it is the same open issue.
Releases
Three separate facts, once your builds report a release:
- Release — the build the latest occurrence came from. This moves as you deploy.
- First seen in — the build the first occurrence came from. Written once, never overwritten.
- Regressed in — the build a closed issue came back in.
Together they read as "introduced in v1.3.0, now on v1.4.2", which is usually the whole investigation.
Setting the release is your job, and there is no default. Inventing one would fill the column with a value that makes "which release introduced this" look answered when it is not.
- Any site: set
window.StorePilot = { release: 'v1.4.2' }before the StorePilot script tag. - WordPress: return it from the
storepilot_releasefilter — for example from your theme'sfunctions.php, reading whatever your build already writes. The browser's errors and the plugin's PHP error reports both carry it.
The same string is what matches an uploaded source map, so a symbolicated stack trace depends on it being the build you actually shipped. See Source Maps.
The detail page
Opening an issue shows the full message and severity, where and when it occurred, the stack trace — symbolicated into your original source when a matching source map has been uploaded — the browser context captured at the time, and the sessions that hit it. Each linked session with a recording carries a link straight into the replay, seeked to the moment it fired.
Assigning and discussing
An issue has an owner and a thread.
The assignee pill in the header reads Unassigned or Assigned: [name], and the picker offers the members of the organization that owns this site — all of them, including viewers. An assignment to anyone outside that organization is refused by the server, not just hidden by the picker.
The Comments panel at the bottom of the page is the team thread. Notes are kept oldest-first and cannot be deleted once posted.
Both assigning and commenting need the member role — the same bar as changing status, because triage is why a member is invited in the first place. Reading the thread needs only view access.
A comment outlives its author's account. The author's address is copied onto the comment when it is written, so if that person later leaves and their account is deleted, the note stays and still says who wrote it.
Linking an issue to Jira, GitHub or Linear is deliberately not built.