Why this matters
A stack trace tells you the line that threw. It does not tell you what the user did to get there. “TypeError on line 88” — but did they click checkout? Search? Toggle a filter? Three different paths, three different fixes.
In a Sentry+Mixpanel world you have errors in one tool and behavior in another. The data lives in different databases with different user identifiers. You eyeball timestamps in two tabs and guess.
Agentry puts both in the same store with the same distinct_id key. One HogQL query does the asof-join: for each error event, find the most recent analytics event by the same user within the previous 60 seconds. The “what did the user just do” answer falls out in one query.
What you get
- For each top-N error fingerprint: the most common analytics event in the 60 seconds before
- A confidence score (how concentrated is the preceding-action distribution?)
- A “reproduction recipe” for the top error: “click X, click Y, then the bug fires”
Walk through it
For each top error fingerprint, find what user action immediately preceded it.
Let me pull the top error fingerprints first.
agentry_list_cases {
project_id: "default",
status: "open",
since: "7d",
limit: 5,
order_by: "event_count"
}
{
"cases": [
{ "id": "c_f72a", "fingerprint": "f_72a", "message": "TypeError on user.plan", "event_count": 412 },
{ "id": "c_2d8e", "fingerprint": "f_2d8e", "message": "checkout 500: stripe timeout", "event_count": 89 },
{ "id": "c_91dd", "fingerprint": "f_91dd", "message": "Failed to load /api/projects", "event_count": 64 }
]
}
Now the asof-join: for each error, what analytics event happened in the 60s before?
agentry_analytics_query {
project_id: "default",
hogql: "WITH errors AS (SELECT distinct_id, timestamp AS err_ts, properties.fingerprint AS fp FROM events WHERE event = '$exception' AND properties.fingerprint IN ('f_72a','f_2d8e','f_91dd') AND timestamp >= now() - INTERVAL 7 DAY), preceding AS (SELECT e.fp AS fp, a.event AS preceding_event, e.distinct_id AS did, a.timestamp AS act_ts, e.err_ts FROM errors e ASOF LEFT JOIN events a ON e.distinct_id = a.distinct_id AND a.timestamp <= e.err_ts WHERE a.event != '$exception' AND a.timestamp >= e.err_ts - INTERVAL 60 SECOND) SELECT fp, preceding_event, count() AS cnt FROM preceding GROUP BY fp, preceding_event ORDER BY fp, cnt DESC"
}
{
"rows": [
{ "fp": "f_72a", "preceding_event": "clicked_account_settings", "cnt": 388 },
{ "fp": "f_72a", "preceding_event": "page_loaded:/settings", "cnt": 16 },
{ "fp": "f_72a", "preceding_event": "$pageview", "cnt": 8 },
{ "fp": "f_2d8e", "preceding_event": "clicked_checkout_button", "cnt": 84 },
{ "fp": "f_2d8e", "preceding_event": "submitted_payment_form", "cnt": 5 },
{ "fp": "f_91dd", "preceding_event": "clicked_projects_tab", "cnt": 42 },
{ "fp": "f_91dd", "preceding_event": "$pageview", "cnt": 18 },
{ "fp": "f_91dd", "preceding_event": "applied_filter", "cnt": 4 }
]
}
Clean reproduction recipes for all three:
f_72a(TypeError on user.plan): 94% triggered byclicked_account_settings. Repro: log in as a free-tier user, click account settings, bug fires. The 6% “page_loaded:/settings” is bookmark/deep-link traffic — same path.f_2d8e(checkout 500): 94% triggered byclicked_checkout_button. Repro: click checkout. Not even the payment form — the failure is server-side before form render.f_91dd(Failed to load /api/projects): 65% triggered byclicked_projects_tab. The 30%$pageviewis direct navigation. The 6%applied_filteris interesting — there might be a filter combination that triggers a slower variant of the same bug.
For f_72a, want me to dig into one specific user’s session?
The output
Reproduction recipes (top 3 errors, 7d)
f_72a TypeError on user.plan
Trigger: clicked_account_settings (94%)
Repro: [log in as free tier] → [click Account Settings]
Confidence: HIGH
f_2d8e checkout 500: stripe timeout
Trigger: clicked_checkout_button (94%)
Repro: [add item to cart] → [click Checkout]
Confidence: HIGH
f_91dd Failed to load /api/projects
Trigger: clicked_projects_tab (65%)
$pageview /projects direct (29%)
applied_filter (6%) ← worth investigating
Repro: [click Projects tab] — usually
[apply a filter] — sometimes, maybe a slower variant
Confidence: MEDIUM (multi-modal)
Next: f_91dd's filter sub-pattern is suspicious. Want me to find which
specific filter values are over-represented in the 6%?
Setting it up
The recipe requires three things:
- Errors and analytics in the same project — true by default with Agentry, both go to the same DSN.
- The same
distinct_idon both — setuser.idon error events to match what you use for analytics. - Useful analytics events —
$pageviewis fine butclicked_Xsemantic events make the repro recipe specific.
// Set distinct_id on every error
await fetch(`https://api.agentry.sh/v1/logs/${PROJECT_ID}/`, {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.AGENTRY_DSN}`,
"Content-Type": "application/json",
"User-Agent": "myapp/1.0", // REQUIRED — Cloudflare 403s default UAs
},
body: JSON.stringify({
message: err.message,
stack: err.stack,
user: { id: currentUser?.email ?? visitorId }, // ← same as analytics
}),
});
// Send semantic events on key interactions (not just $pageview)
function trackClick(eventName: string, properties?: Record<string, unknown>) {
return fetch(`https://api.agentry.sh/v1/analytics/${PROJECT_ID}/`, {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.AGENTRY_DSN}`,
"Content-Type": "application/json",
"User-Agent": "myapp/1.0",
},
body: JSON.stringify({
event: eventName,
distinct_id: currentUser?.email ?? visitorId,
properties,
}),
});
}
// In your UI:
<button onClick={() => { trackClick("clicked_checkout_button"); goToCheckout(); }}>
Checkout
</button>
$pageview alone gives you “they were on /settings” but clicked_account_settings gives you “they clicked the menu item.” For the asof-join to produce useful repro recipes, lean toward semantic events.
Variations
- “For error
c_f72a, give me the FULL pre-error session of 3 specific affected users — not just the last action.” - “Build the asof-join the OTHER way: for users who triggered checkout, what % went to error vs success?”
- “Find error fingerprints with NO preceding analytics event in the last 60s. Those are server-internal (cron, webhook).”
- “Find errors where the preceding action distribution is bimodal — multiple distinct repro paths.”