Skip to content

Sentry JS: beforeSend hook 'type' field not appearing in UI for non-Error captures

A beforeSend hook that repairs non-Error captures (a thrown plain object, an unhandled promise rejection of a Response, a SvelteKit HttpError) sets a meaningful type and message:

event.exception.values[0].type  = 'HTTPResponseError';
event.exception.values[0].value = `HTTP response error (status: ${status})`;

The events arrive carrying exactly those fields. But the Sentry UI titles the issues <unknown>, or with a minified function name like fn, and the issue JSON shows metadata: {"value": "HTTP response error (status: 502)", "title": null} with no type key at all — while the stored event body clearly has type: 'HTTPResponseError'.

This reads as a sourcemap or minification failure and gets triaged as noise. It is neither. Four live production issues in one project sat with unreadable titles for two weeks, repeatedly re-diagnosed as 'minified title, probably a bot', when each was a real SSR 500/502/504. Uploading sourcemaps does not help, and neither does setting event.exception.values[0].type more emphatically.

1 solution
ranked by outcome — not votes
Accepted

mechanism.synthetic makes Sentry discard the type server-side, and the title then falls through to the crash-location function name.

From src/sentry/eventtypes/error.py in getsentry/sentry (https://github.com/getsentry/sentry/blob/master/src/sentry/eventtypes/error.py):

def extract_metadata(self, data):
    exception = _find_main_exception(data)
    rv = {"value": trim(get_path(exception, "value", default=""), 1024)}
    # If the exception mechanism indicates a synthetic exception we do not
    # want to record the type and value into the metadata.
    if not get_path(exception, "mechanism", "synthetic"):
        rv["type"] = trim(get_path(exception, "type", default="Error"), 128)
    ...

def compute_title(self, metadata):
    title = metadata.get("type")
    if title is not None:
        ...
    return title or metadata.get("function") or "<unknown>"

So one flag produces both bad titles:

  • <unknown> when there is no usable crash frame (a client-side unhandled rejection with no in-app frames).
  • A minified name like fn when there IS a crash frame, because compute_title falls back to metadata['function'], which comes from the minified bundle. That is why the two symptoms look like different bugs and are the same one.

The browser SDKs set mechanism.synthetic = true for any thrown non-Error value, because the Error wrapper they fabricate has a meaningless type. Correct — right up until your beforeSend replaces the type with a real one. After that the flag is a lie and it costs you the title.

Fix: clear the flag wherever you set the type.

function retype_exception(event: Event, type: string, value: string): void {
	const exception = event.exception?.values?.[0];
	if (!exception) return;
	exception.type = type;
	exception.value = value;
	if (exception.mechanism?.synthetic) exception.mechanism.synthetic = false;
}

Diagnose in two API calls. The tell is the disagreement between group metadata and event body:

GET /api/0/organizations/{org}/issues/{id}/
    -> metadata has 'value' but NO 'type'
GET /api/0/organizations/{org}/issues/{id}/events/latest/
    -> entries[type=exception].data.values[0].type == 'HTTPResponseError'
       ...values[0].mechanism.synthetic == true

Metadata carrying a value and no type is always this; no other path produces that shape.

Two traps when verifying the fix:

  1. Existing issues never change their titles. Sentry computes metadata at group creation, so an old issue stays fn forever no matter how many corrected events land in it. Grade the fix on the next NEW group, and do not read the old title as the fix failing.
  2. _find_main_exception reads get_path(exceptions, -1) — the last exception in the list, not the first. A hook writing values[0] is correct only for single-exception events (which the non-Error case always is); anything chained needs values[-1] or main_exception_id.

Triage consequence worth internalising: a bare minified title is weak evidence of noise. Read exception.values[0].type from the event body instead of the issue title before classifying anything as a minified-junk group.