Menu
Platform
AI
App store purchases
Database
Flags
Jobs and cron
Localization
Monitoring
Notifications
Payments
Queues
Sandboxes
Webhooks
Getting Started
Authentication
KV Store
Deploy & Infrastructure
Reference
Errors
Grouping, capture, and the three calls that move a group through triage
An error arrives as an occurrence and is read as a group. The occurrence is never changed once stored; what moves is the group, and only through the calls on this page. The error_groups collection has every method, its scope and its examples.
#How a group is chosen
An Error Group is the deterministic grouping of error occurrences by
fingerprint. A capture is grouped by fingerprint when the caller sets one, and
otherwise by exception type and the symbolicated in-app stack.
Release is never part of the group key. last_release reports the release of
the latest occurrence, and nothing else, which is why one bug stays one group
across releases rather than starting again at every deploy.
Everything a group reports is derived from its occurrences:
| Field | Type | What it is |
|---|---|---|
fingerprint | string | The grouping fingerprint. Output only. |
title | string | The exception type and message of the first occurrence. Output only. |
culprit | string | The top in-app frame. Output only. |
service_name | string | The emitting service. Output only. |
state | ErrorGroupState | The triage state: unresolved, acknowledged or resolved. Output only. |
first_seen_time | timestamp | The first occurrence. Output only. |
last_seen_time | timestamp | The latest occurrence. Output only. |
occurrence_count | int64 | Occurrences, all time. Output only. |
last_release | string | The release of the latest occurrence. Output only. |
#Capture an occurrence
capture takes one occurrence. event_time and exception_type are required;
everything else sharpens the group or the record.
| Field | Type | What it is |
|---|---|---|
event_time | timestamp | When it happened. Required. |
exception_type | string | The exception type. Required. |
message | string | The exception message. |
stack | string | The stack as the runtime printed it. Scrubbed before storage. |
stack_frames | StackFrame[] | Stack frames, innermost last. When set, it wins and stack is kept as sent. |
service_name | string | The emitting service. |
release | string | The release the emitter ran. It selects the source maps the frames are mapped through. |
breadcrumbs | Breadcrumb[] | Breadcrumbs before the error, oldest first. |
tags | map<string, string> | Typed tags. |
fingerprint | string | Overrides the grouping fingerprint. |
trace_id | string | W3C trace id (32 hex), which connects the occurrence to a trace. |
span_id | string | W3C span id (16 hex). |
sylphx observability error-groups capture orgs/acme/projects/shop/envs/productioncurl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/error_groups:capture" \
-H "Authorization: Bearer $SYLPHX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"error_event":{"event_time":{},"exception_type":"…"}}'const response = await sylphx.observability.errorGroups.capture({ errorEvent: { eventTime: {}, exceptionType: '…' }, parent: 'orgs/acme/projects/shop/envs/production' })The stack is read for you
When stack_frames is empty, V8 lines (at fn (file:line:col)) and
Gecko/WebKit lines (fn@file:line:col) are parsed into frames, innermost last.
When stack_frames is set, it wins and stack is kept as sent. Either way the
frames are mapped through the source maps uploaded for release before the
group is chosen — Source maps is the upload.
Secrets and personal data are scrubbed by the environment's
Scrubbing Policy before the occurrence is stored,
and stack is scrubbed with the rest.
#Read a group and its occurrences
An Error Event is one immutable error occurrence, with breadcrumbs and correlation, and it lives under the group it joined. Nothing rewrites an occurrence, so a group's history cannot be edited — only its state moves.
sylphx observability error-groups list orgs/acme/projects/shop/envs/production
sylphx observability error-groups get orgs/acme/projects/shop/envs/production/error_groups/error-group
sylphx observability error-events list orgs/acme/projects/shop/envs/production/error_groups/error-group
sylphx observability error-events get orgs/acme/projects/shop/envs/production/error_groups/error-group/error_events/error-eventListing groups filters by state, service_name and last_seen_time, which is
how a triage list is built: the groups still unresolved, for one service, seen
in the interval you care about.
An occurrence carries what the capture sent — its breadcrumbs oldest first, its
tags, its release — and the trace_id and span_id that connect it to a
trace. Together those are the difference between knowing a group fired and
knowing what the request was doing when it did.
#Triage a group
Acknowledge, resolve and reopen require the current etag: read the group, and
send the etag you read back with the call. A call whose etag no longer
matches fails with ETAG_MISMATCH and changes nothing, which is what stops two
people triaging the same group from overwriting each other.
sylphx observability error-groups acknowledge orgs/acme/projects/shop/envs/production/error_groups/error-group --etag …curl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/error_groups/error-group:acknowledge" \
-H "Authorization: Bearer $SYLPHX_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'const response = await sylphx.observability.errorGroups.acknowledge({ name: 'orgs/acme/projects/shop/envs/production/error_groups/error-group' })acknowledge says the group is seen and being worked on. resolve says it is
done — and a new occurrence reopens it, so a resolve is a statement about now
rather than a promise about later:
sylphx observability error-groups resolve orgs/acme/projects/shop/envs/production/error_groups/error-group --etag …curl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/error_groups/error-group:resolve" \
-H "Authorization: Bearer $SYLPHX_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'const response = await sylphx.observability.errorGroups.resolve({ name: 'orgs/acme/projects/shop/envs/production/error_groups/error-group' })reopen takes a resolved or an acknowledged group back to triage, which is the
call to reach for when the bug was not fixed after all:
sylphx observability error-groups reopen orgs/acme/projects/shop/envs/production/error_groups/error-group --etag …curl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/error_groups/error-group:reopen" \
-H "Authorization: Bearer $SYLPHX_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'const response = await sylphx.observability.errorGroups.reopen({ name: 'orgs/acme/projects/shop/envs/production/error_groups/error-group' })A call the group's state forbids — reopening one that is already unresolved —
fails with INVALID_STATE. The error_groups commands
show the --etag flag on each of the three.