feat(telemetry): record errorCode/errorType on dashboard_error_created

Wire categorizeError into the three error-display tracking points
(AlertError, ErrorMatcher, ToastErrorTracker) and add the PII-free
errorCode/errorType fields to the event type. Lets us see which errors
occur per route without storing raw messages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jordi EnricandClaude Opus 4.8 committed 2026-06-15 17:40:50 +02:00
1 parent 2abfd556c2
commit 9c5ed71ca5
4 files changed
+19 -2

No files matched your search

@@ -3,6 +3,7 @@
import { ErrorDisplay, SupportFormParams } from 'ui-patterns/ErrorDisplay/ErrorDisplay'
import { getMappingForError } from './ErrorMatcher.utils'
import { categorizeError } from '@/lib/telemetry/categorizeError'
import { useTrack } from '@/lib/telemetry/track'
interface ErrorMatcherProps {
@@ -29,7 +30,7 @@ export function ErrorMatcher({ title, error, supportFormParams, className }: Err
if (Math.random() < 0.1) {
track('dashboard_error_created', {
source: 'error_display',
errorType: mapping?.id,
...categorizeError(error),
hasTroubleshooting: !!mapping,
})
}
+2
View File
@@ -4,6 +4,7 @@ import { Button } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { SupportLink } from '@/components/interfaces/Support/SupportLink'
import { categorizeError } from '@/lib/telemetry/categorizeError'
import { useTrack } from '@/lib/telemetry/track'
export interface AlertErrorProps {
@@ -73,6 +74,7 @@ export const AlertError = ({
if (Math.random() < 0.1) {
track('dashboard_error_created', {
source: 'admonition',
...categorizeError(error),
})
}
}
+6
View File
@@ -1,6 +1,7 @@
import { useEffect, useRef } from 'react'
import { useSonner } from 'sonner'
import { categorizeError } from '@/lib/telemetry/categorizeError'
import { useTrack } from '@/lib/telemetry/track'
export const ToastErrorTracker = () => {
@@ -13,8 +14,13 @@ export const ToastErrorTracker = () => {
if (toast.type === 'error' && !trackRef.current.has(toast.id)) {
trackRef.current.add(toast.id)
if (Math.random() < 0.1) {
// Only the rendered title is available here and it may contain PII, so
// categorizeError extracts a PII-free code/type and never the text.
// Non-string titles (ReactNode) yield `errorType: 'unknown'`.
const title = typeof toast.title === 'string' ? toast.title : ''
track('dashboard_error_created', {
source: 'toast',
...categorizeError(title),
})
}
}
+9 -1
View File
@@ -2821,9 +2821,17 @@ export interface DashboardErrorCreatedEvent {
*/
source?: 'admonition' | 'toast' | 'error_display'
/**
* Type of error matched (for error_display source)
* Normalized, PII-free error category derived from the error
* (e.g. 'connection-timeout', 'unauthorized', 'forbidden', 'not-found',
* 'server-error', 'network-error', 'timeout', 'canceled', 'unknown').
* Never contains the raw error message.
*/
errorType?: string
/**
* HTTP status code (e.g. 403, 500) when available, or a transport category
* ('network', 'timeout') when there is no status. PII-free.
*/
errorCode?: number | string
/**
* Whether troubleshooting steps are available (for error_display source)
*/