# How we now know which commit broke each Sentry error

Five Sentry tickets in one worker turned out to be one bug. The most-recent error came from the very PR that had wired Sentry forwarding in. How a stack frame now leads to the offending commit, the deploy behind it, and the team that owns the failing path.

Author: Louis Fradin

Published: 2026-05-11

Source: https://anyshift.io/blog/how-we-now-know-which-commit-broke-each-sentry-error

The first question we asked Annie after wiring up the Sentry integration on a Tuesday morning returned **sixty-seven unresolved errors**. We'd been ready to write the "we shipped the integration and Sentry is empty" post. That post is now this post! They came back grouped by service, each one already attached to the deploy that introduced it. And in one worker service, **five sibling errors** that looked distinct in the Sentry UI were sitting under one finding.

The integration shipped May 11, 2026. Annie reads Sentry events and resolves each one against Anyshift's production resources graph.

## What a Sentry stack frame becomes in the graph

Sentry gives you the stack frame, event count, release tag, and users impacted. The graph adds the rest of the chain: a `release` tag resolves to a deploy node, joined to the commit that introduced it, the PR that shipped that commit, the team the resource is attributed to (not "whoever committed last"), and the blast radius, which services share the failing path and would be next.

That cascade runs both ways. Ask Annie *"what would break if `payments-api` goes down right now"* and Annie answers from live Sentry signal rather than a stale architecture diagram. The events become nodes in the same graph as the deploys, IAM changes, and commits they came from.

## The first surprise: 5 Sentry tickets in one worker were one bug

We asked one question:

> List the most recent critical Sentry errors across our stack. Group them by service, show me the stack trace of the most recent one, and tell me which release introduced it if you can.

Eighty-four seconds:

Five Sentry tickets dropped to one because the graph saw what the Sentry UI couldn't: same upstream queue, same failure path, **one bug**. We had been about to file five separate tickets in Linear.

The queue-consumer worker cluster was where we went first. Five issues in Sentry, all sibling variants of the same parent. Sentry had them as five distinct tickets with five distinct fingerprints, because the fingerprinter keys off stack signature and the variants had different ones. The graph collapsed them into one finding because they shared a root cause Annie surfaced without being asked.

Annie then surfaced three follow-ups on the most recent issue. The first:

> *Dig into the MarshalerError to find which release introduced it.*

A `json.MarshalerError: unexpected end of JSON input`, raised inside one of our backend services' fanout handlers. The graph already knew which deploy had touched that code path before we asked. We clicked through to the deploy. The PR title was `chore: add sentry`, the commit that had wired Sentry forwarding into our slog handler in the first place. Some slog attribute couldn't be marshaled to JSON, and the panic surfaced one layer up inside the fanout. **One click, not five!**

Sixty-seven unresolved errors on day one is the number you don't want to learn about your own stack. Two of them turned out to be from the Linear integration we'd shipped in April: startup retries the agent makes when Linear isn't reachable on the first try. They recover on the next tick. They had also been firing as Error-level into Sentry the entire time, **routed to a Slack channel we'd muted three weeks earlier**, and nobody had opened Sentry for that service since the day after it shipped.

## From five tabs to one Annie question

Instead of opening five tabs to triage one alert, you ask one question. A `release` tag now resolves into a deploy, a commit, a PR, an owner, an environment, an IAM change that landed at the same window. Sentry tells you what broke. **The graph tells you what that means for everything else.**

*Setup: [docs.anyshift.io/integration/sentry](https://docs.anyshift.io/integration/sentry)*

