Skip to content

Every error your app sends is stored as an exception — one row per occurrence. An issue is a group of exceptions that are the same problem: the same error thrown from the same place, again and again. The project's Issues page lists these groups, so a bug that fired ten thousand times shows up once, with a counter, instead of flooding the list.

Issues are built on the server from the exception events your SDK sends (see Capturing Errors and the exception event). There's nothing to enable in the SDK — every captured exception lands in an issue by default.

How exceptions are grouped ​

When an exception arrives, Buglapse renders the project's fingerprint template against it and hashes the result. Exceptions with the same fingerprint belong to the same issue. The default template is:

text
{{event.name}}::{{event.message}}

so TypeError: Cannot read properties of undefined (reading 'id') is one issue no matter how many users hit it, while a TypeError with a different message is another.

A fingerprint is scoped to its project: the same error in two projects makes two separate issues.

The first exception with a new fingerprint creates the issue. Every later one updates it:

FieldHow it's kept
EventsThe number of stored exceptions with this fingerprint.
UsersThe number of distinct identified users among them.
First seen / Last seenThe earliest and latest occurrence.
EnvironmentThe environment of the latest occurrence.
First releaseThe release of the exception that created the issue — never changes afterwards. The release page counts it as a new issue of that release.

Counters are recomputed from the stored exceptions rather than incremented, so retries on the server never double-count an occurrence.

Customize the fingerprint ​

Open the project's Settings → Fingerprinting. The Exception fingerprint field is a template with {{...}} placeholders resolved from the ingested event; the buttons under it insert the common ones:

PlaceholderValue
{{event.name}}The error's name, e.g. TypeError.
{{event.message}}The error's message.
{{event.environment}}The event's environment.
{{event.release}}The release the event came from.
{{event.tags.<key>}}A tag you set in the SDK, e.g. {{event.tags.env}}.
{{event.contexts.<key>}}A context value, e.g. {{event.contexts.browser.name}} from the parsed User-Agent.
{{event.frames_signature}}A ready-made stack-trace grouping key: the top 5 stack frames as function@file, joined with ::, innermost (throw site) first. Built from the same parsed stack the exception event's stack field carries.
{{event.frames.<n>.function}}, {{event.frames.<n>.file}}A single parsed stack frame, 0-indexed from the top — e.g. {{event.frames.0.function}} is the function that threw.

A placeholder that doesn't resolve renders as an empty string. Some recipes:

  • {{event.name}} — one issue per error type, however the message varies (useful when messages embed ids or timestamps).
  • {{event.name}}::{{event.message}}::{{event.environment}} — keep production and staging apart.
  • {{event.name}}::{{event.message}}::{{event.contexts.browser.name}} — split a browser-specific bug per browser.
  • {{event.name}}::{{event.frames_signature}} — group by call site instead of by message, so a shifted line number after a deploy doesn't start a new issue. Note that for a minified, content-hashed browser bundle (app.3f9a1c.js), the file name itself usually changes on every deploy that touches that bundle, so this still fragments the issue across deploys — it mainly helps within a single build (e.g. the same error hit from two different requests) or for unminified/Node stacks where the file path is stable.

The template applies to exceptions received after you save it. Existing exceptions keep their fingerprints, so after a change the same error may start a new issue next to the old one.

Choose which exceptions become issues ​

The Issue collection field on the same page is an expression evaluated for every exception. When it's true, the exception is grouped into an issue; when it's false, the exception is still stored (and still counts toward its release) but doesn't create or update an issue. The default collects everything:

text
"{{event.type}}" === "exception"

For example, to only open issues for production errors:

text
"{{event.environment}}" === "production"

A placeholder in double quotes ("{{event.environment}}") is compared as a string; a bare one ({{event.tags.level}}) keeps its raw value, or null when the event doesn't have it. Values from the event are only ever compared — whatever an error message contains, it can't change the expression. Expressions support strings, numbers, true, false, null, ===, !==, ==, !=, <, >, <=, >=, !, && (or and), || (or or), and parentheses. An expression that doesn't parse is rejected when you save it.

Issue keys ​

Each issue gets a short key made of the project's slug and a number, like MY-APP-42. Numbers are handed out per project, in the order issues are created, and never reused — deleting MY-APP-42 doesn't give its number to the next issue. The key is the issue page's title; copy key next to it grabs it for a commit message, a ticket, or a chat.

The issues list ​

The project's Issues page shows every issue, most recently seen first. Each row shows the error, its key, the number of events and affected users, a sparkline of occurrences over the last 24 hours, when it was last seen, its assignee, and whether it's resolved.

Use the search box above the list to narrow it down — e.g. timeout environment:production resolved:false. The query syntax and every key it accepts on this page (issue:, resolved:, environment:, first_release:) are described on the Search page.

The issue page ​

Opening an issue shows the latest occurrence in full, plus stats across recent ones:

  • Errors — occurrences over time since the issue was first seen, with a link to the issue's exceptions on the Exceptions page.
  • Stack trace — from the latest exception, resolved to original files and lines when the release has source maps.
  • Breadcrumbs — what happened right before the error (see Breadcrumbs).
  • Trace — the trace the error happened in, when there is one.
  • Session replay — a recording of the user's session leading up to the error, when the ReplayIntegration captured one.
  • Autofix — the state of an AI agent's run on this issue (see Autofix).
  • Details — first and last seen, the affected user, the trace id, and how the latest 50 occurrences split by tag and by context (browser, OS, device, and so on).

Triage ​

Resolve ​

Mark as resolved once a fix has shipped. A resolved issue stays in the list, marked with a check, and can be filtered out with resolved:false.

If the same error comes back — a new exception with the issue's fingerprint arrives — the issue is reopened automatically: it's marked unresolved again and its counters and last seen move on. Nothing else is lost, so a regression shows up in the list with its whole history.

Assign ​

Every issue can have one assignee: a member of the organization, or an AI agent. Pick it from the assignee menu on the issue page or in the list. Assigning an issue to an agent starts an Autofix run; assigning it back to a member, or unassigning it, cancels that run.

Delete ​

Delete removes the issue together with every exception grouped into it — use it for noise you don't want to keep, not for fixed bugs (resolve those instead). It can't be undone. If the error happens again afterwards, it starts a new issue with a new key.

Deleting a single exception keeps the issue and recomputes its counters; deleting its last exception removes the issue too.

From your AI assistant ​

The MCP server exposes issues to AI clients: list-issues takes the same search syntax as the dashboard, and get-issue returns an issue with its 5 most recent occurrences.