Skip to content

Crash-free metric issues do not show correlated issues: detector ID used as alert rule ID #116134

@sentry-junior

Description

@sentry-junior

Problem

When a crash-free rate metric alert is represented as a metric issue (new detector-backed issue type), the "Correlated Issues" section does not appear — or appears but fails silently. This is a regression from the old metric alert detail page, which reliably showed a "Related Issues" section filtered by error.unhandled:true.

Background: old behavior

On the old metric alert detail page (/alerts/metric-rules/{ruleId}/), the alert rule object is loaded directly from the URL's rule ID. For crash-free rate alerts (Dataset.SESSIONS or Dataset.METRICS), body.tsx always renders:

// static/app/views/alerts/rules/metric/details/body.tsx
{[Dataset.METRICS, Dataset.SESSIONS, Dataset.ERRORS].includes(dataset) && (
  <RelatedIssues
    rule={rule}
    query={isCrashFreeAlert(dataset) ? `${query} error.unhandled:true`.trim() : undefined}
    timePeriod={timePeriod}
    ...
  />
)}

Because the rule is loaded by alert rule ID from the URL, this section always worked.

New behavior: two failure modes in MetricIssuesSection

Failure mode 1 — section never renders

In groupEventDetailsContent.tsx, MetricIssuesSection only mounts when:

{(event.contexts?.metric_alert?.alert_rule_id ||
  event?.occurrence?.evidenceData?.alertId) && (
  <MetricIssuesSection ... />
)}

For detector-backed metric issues (occurrence type 8001), these legacy fields may not be populated. getDetectorDetails() in detectorSection.tsx correctly identifies such issues as detectorType: 'metric_alert' — but that check is not used in the render condition. Session-based (crash-free rate) metric issues are especially likely to be missing those legacy fields because there is no underlying error event to carry them.

Failure mode 2 — section renders but silently fails to load the rule

Even when the section does render, MetricIssuesSection resolves the metric alert rule like this:

// static/app/views/issueDetails/metricIssues/metricIssuesSection.tsx
const ruleId = useMetricIssueAlertId({groupId: group.id});
const {data: rule} = useMetricRule({orgSlug, ruleId});

useMetricIssueAlertId (metricIssues/utils.tsx) does:

const hasMetricDetector = detectorId && detectorType === 'metric_alert';
// detectorId comes from event.occurrence?.evidenceData.detectorId — the workflow engine detector ID
return hasMetricDetector ? detectorId : fallback;

useMetricRule then hits:

GET /organizations/{org}/alert-rules/{alertRuleId}/

The problem: the code passes the workflow-engine detector ID to an endpoint that expects an alert rule ID. These are different entities with different numeric IDs. detectorSection.tsx already acknowledges this mapping problem — its MetricAlertSection fetches the detector object and reads metricDetector.alertRuleId to get the actual alert rule ID. No equivalent translation exists in MetricIssuesSection.

When useMetricRule receives the wrong ID it returns nothing, MetricIssuesSection returns null, and the correlated issues panel is invisible for crash-free metric issues.

Why crash-free rate is most affected

  • Crash-free rate alerts use Dataset.SESSIONS — they fire on session-derived percentages, not on individual error events.
  • When a crash-free metric issue is created, its occurrence likely lacks the legacy alert_rule_id / alertId fields (no error event to attach them to), so failure mode 1 is more likely to trigger.
  • Even if the section does render, the detector ID → alert rule ID mismatch (failure mode 2) silently breaks rule loading.

Proposed fixes

1. Fix the render condition (failure mode 1)

Also gate on detector metadata so detector-backed metric issues are not excluded:

// groupEventDetailsContent.tsx
const isMetricIssue =
  event.occurrence?.type === 8001 ||
  detectorDetails.detectorType === 'metric_alert' ||
  !!event.contexts?.metric_alert?.alert_rule_id ||
  !!event?.occurrence?.evidenceData?.alertId;

{isMetricIssue && <MetricIssuesSection ... />}

2. Fix alert rule ID resolution (failure mode 2)

useMetricIssueAlertId should resolve the alert rule ID — not the detector ID:

// Current — wrong when hasMetricDetector is true:
return hasMetricDetector ? detectorId : fallback;

// Better — prefer alert rule ID fields, fall back to a detector→rule lookup:
return (
  event?.occurrence?.evidenceData?.alertId ??
  event?.contexts?.metric_alert?.alert_rule_id ??
  metricDetector?.alertRuleId  // via /alert-rule-detector/ like MetricAlertSection
);

Alternatively, MetricIssuesSection could use the /organizations/{org}/alert-rule-detector/?alert_rule_id=... endpoint (already used by MetricAlertOngoingIssues) to translate detector ID → alert rule ID before calling useMetricRule.

3. Backend hardening (optional)

Ensure crash-free rate metric issue occurrences (type 8001) always populate evidenceData.alertId with the linked alert rule ID, making the frontend fallback path reliable without a secondary lookup.

Files involved

File Role
static/app/views/issueDetails/groupEventDetails/groupEventDetailsContent.tsx Controls whether MetricIssuesSection renders
static/app/views/issueDetails/metricIssues/metricIssuesSection.tsx Correlated issues section for metric issues
static/app/views/issueDetails/metricIssues/utils.tsxuseMetricIssueAlertId Resolves rule ID (currently returns detector ID instead)
static/app/views/alerts/rules/metric/utils/useMetricRule.tsx Fetches alert rule by ID
static/app/views/alerts/rules/metric/details/relatedIssues.tsx Shared RelatedIssues component
static/app/views/issueDetails/streamline/sidebar/detectorSection.tsx getDetectorDetails sets detectorType: 'metric_alert' correctly but it is unused in the render gate

Secondary observation

isCrashFreeAlert(dataset) returns true for both Dataset.SESSIONS and Dataset.METRICS. If any non-crash-free alert uses Dataset.METRICS, those alerts would incorrectly have error.unhandled:true appended to their correlated-issues query. Worth auditing separately once the primary bug is fixed.


View Session in Sentry

Metadata

Metadata

Assignees

No one assigned
    No fields configured for issues without a type.

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions