File size: 1,545 Bytes
a070805 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | import { trackEvent } from "#/services/telemetry";
import type { ErrorClassification } from "@openhands/typescript-client";
interface ErrorDetails {
source?: string;
metadata?: Record<string, unknown>;
classification?: ErrorClassification | null;
}
const RESERVED_ERROR_KEYS = new Set([
"error_source",
"error_kind",
"error_id",
"error_telemetry",
]);
export function trackError({
source,
metadata = {},
classification,
}: ErrorDetails) {
// Reserved outcome fields are derived from `source`/`classification` and
// must not be overridable through arbitrary caller metadata.
const extra = Object.fromEntries(
Object.entries(metadata).filter(([key]) => !RESERVED_ERROR_KEYS.has(key)),
);
const kind = classification?.kind || "unknown";
// Promote a caller-provided `eventId` to the reserved `error_id` dimension as
// a fallback so unclassified errors stay correlatable with server-side logs.
const promotedEventId =
typeof metadata.eventId === "string" ? metadata.eventId : undefined;
if (promotedEventId != null) {
delete extra.eventId;
}
const errorId = classification?.error_id ?? promotedEventId;
void trackEvent("error_outcome", {
...extra,
current_url: window.location.href,
error_source: source || "unknown",
error_kind: kind,
// Keep diagnostic errors correlatable without capturing raw messages.
...(errorId != null ? { error_id: errorId } : {}),
error_telemetry:
kind === "internal" || kind === "unknown" ? "diagnostic" : "outcome",
});
}
|