Documentation Index Fetch the complete documentation index at: https://mintlify.com/ghuntley/loom/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Loom’s crash tracking system captures errors and crashes from TypeScript (browser + Node.js) and Rust applications, groups them into issues via fingerprinting, tracks regressions across releases, and provides source context through uploaded source maps.
Crash tracking integrates with analytics for person identity and feature flags to capture active flags at crash time.
Key Features
Multi-platform crash capture: JavaScript, Node.js, Rust
Issue grouping via fingerprinting to deduplicate similar crashes
Issue lifecycle with unresolved/resolved/ignored/regressed states
Regression detection when resolved issues reappear
Source map uploads for TypeScript with source context
Rust backtrace symbolication with demangling
Core Concepts
Crash Event
A single crash occurrence:
pub struct CrashEvent {
pub id : CrashEventId ,
pub org_id : OrgId ,
pub project_id : ProjectId ,
pub issue_id : Option < IssueId >,
// Identity
pub person_id : Option < PersonId >,
pub distinct_id : String ,
// Error
pub exception_type : String ,
pub exception_value : String ,
pub stacktrace : Stacktrace ,
// Context
pub release : Option < String >,
pub environment : String ,
pub platform : Platform ,
pub tags : HashMap < String , String >,
pub active_flags : HashMap < String , String >,
pub breadcrumbs : Vec < Breadcrumb >,
}
Issue
An aggregated group of similar crashes:
pub struct Issue {
pub id : IssueId ,
pub short_id : String , // "PROJ-123"
pub fingerprint : String , // SHA256 hash
pub title : String ,
pub status : IssueStatus ,
pub level : IssueLevel ,
pub event_count : u64 ,
pub user_count : u64 ,
pub first_seen : DateTime < Utc >,
pub last_seen : DateTime < Utc >,
}
Issue statuses:
Status Description UnresolvedNew or ongoing issue ResolvedFixed by developer IgnoredIntentionally ignored RegressedResolved issue reappeared
pub enum Platform {
JavaScript , // Browser
Node , // Node.js
Rust ,
}
Rust SDK
Install the SDK:
[ dependencies ]
loom-crash = { version = "0.1" }
loom-analytics = { version = "0.1" } # Optional: for identity
loom-flags = { version = "0.1" } # Optional: for flag capture
Setup with Integrations
use loom_crash :: CrashClient ;
use loom_analytics :: AnalyticsClient ;
use loom_flags :: FlagsClient ;
// Initialize crash client with integrations
let crash = CrashClient :: builder ()
. api_key ( "loom_crash_xxx" )
. base_url ( "https://loom.example.com" )
. project ( "my-rust-app" )
. release ( env! ( "CARGO_PKG_VERSION" ))
// Integrate with analytics for person identity
. analytics ( & analytics_client )
// Integrate with flags to capture active flags
. flags ( & flags_client )
. build () ? ;
// Install panic hook (captures all panics)
crash . install_panic_hook ();
// Set user context
crash . set_user ( UserContext {
id : Some ( user . id . to_string ()),
email : Some ( user . email . clone ()),
.. Default :: default ()
});
// Add global tags
crash . set_tag ( "environment" , "production" );
crash . set_tag ( "server" , hostname );
Capturing Errors
// Manual capture
if let Err ( e ) = do_something () {
crash . capture_error ( & e );
}
// Capture breadcrumb
crash . add_breadcrumb ( Breadcrumb {
category : "http" . into (),
message : Some ( "GET /api/users" . into ()),
level : BreadcrumbLevel :: Info ,
.. Default :: default ()
});
// Scoped context
crash . with_scope ( | scope | {
scope . set_tag ( "handler" , "create_user" );
scope . set_extra ( "request_id" , json! ( request_id ));
process_request () ? ;
Ok (())
}) ? ;
// Shutdown (flushes pending events)
crash . shutdown () . await ? ;
TypeScript SDK
Install the SDK:
Browser Setup
import { CrashClient } from '@loom/crash' ;
import { AnalyticsClient } from '@loom/analytics' ;
import { FlagsClient } from '@loom/flags' ;
const crash = new CrashClient ({
apiKey: 'loom_crash_xxx' ,
baseUrl: 'https://loom.example.com' ,
project: 'my-web-app' ,
release: '1.2.3' ,
environment: 'production' ,
// Integrate with other SDKs
analytics ,
flags ,
// Options
maxBreadcrumbs: 100 ,
beforeSend : ( event ) => {
// Filter or modify events before sending
if ( event . exception_value . includes ( 'ResizeObserver' )) {
return null ; // Drop this event
}
return event ;
},
});
// Install global handlers
crash . installGlobalHandler ();
// Set user context
crash . setUser ({
id: user . id ,
email: user . email ,
});
Capturing Exceptions
// Manual capture
try {
await riskyOperation ();
} catch ( error ) {
crash . captureException ( error , {
tags: { operation: 'risky' },
extra: { attemptNumber: 3 },
});
}
// Add breadcrumb
crash . addBreadcrumb ({
category: 'navigation' ,
message: 'User navigated to /dashboard' ,
level: 'info' ,
});
React Error Boundary
import { CrashBoundary } from '@loom/crash/react' ;
function App () {
return (
< CrashBoundary
client = { crash }
fallback = {<ErrorPage />}
onError = {( error , errorInfo) => {
console . log ( 'Crash captured:' , error );
}}
>
< MyApp />
</ CrashBoundary >
);
}
Source Maps
Upload source maps to get readable stack traces for minified JavaScript:
Build your app with source maps
// vite.config.ts
export default {
build: {
sourcemap: true
}
}
Upload source maps to Loom
loom crash upload-sourcemaps \
--project my-app \
--release 1.2.3 \
--include ./dist/ ** / * .map \
--include ./dist/ ** / * .js
Deploy your app
Source maps are matched by release version and file paths
Vite plugin example:
// vite.config.ts
import { loomSourceMaps } from '@loom/crash-vite' ;
export default {
plugins: [
loomSourceMaps ({
project: 'my-app' ,
release: process . env . GIT_SHA ,
apiKey: process . env . LOOM_CRASH_API_KEY ,
}),
] ,
} ;
Issue Fingerprinting
Crashes are grouped by fingerprint (SHA256 hash):
Default algorithm:
Exception type (most significant)
Top 5 in-app stack frames (function + module)
If no in-app frames, use all frames
Example:
TypeError | handleClick@App.tsx | onClick@Button.tsx
Custom Fingerprinting
Override fingerprinting rules via API:
{
"fingerprint_rules" : [
{
"match_type" : "exception_message" ,
"pattern" : "rate limit exceeded.*" ,
"fingerprint" : [ "rate-limit-error" ]
},
{
"match_type" : "module" ,
"pattern" : "third_party::*" ,
"fingerprint" : [ "third-party-error" , "{{ module }}" ]
}
]
}
Issue Lifecycle
Unresolved
New crash creates issue in Unresolved state
Resolve
Mark issue as Resolved when fixed: curl -X POST https://loom.example.com/api/crash/projects/proj-123/issues/iss-456/resolve \
-H "Authorization: Bearer <token>" \
-d '{"resolved_in_release": "1.2.4"}'
Regression Detection
If new crash arrives for resolved issue:
Status changes to Regressed
times_regressed increments
Real-time alert sent via SSE
Ignore
Permanently ignore an issue: curl -X POST https://loom.example.com/api/crash/projects/proj-123/issues/iss-456/ignore
API Endpoints
Capture Crashes
Ingest single crash event (requires Capture API key) Request: {
"distinct_id" : "user_123" ,
"exception_type" : "TypeError" ,
"exception_value" : "Cannot read property 'x' of undefined" ,
"stacktrace" : {
"frames" : [
{
"function" : "handleClick" ,
"filename" : "App.tsx" ,
"lineno" : 42 ,
"colno" : 15 ,
"in_app" : true
}
]
},
"release" : "1.2.3" ,
"environment" : "production" ,
"platform" : "javascript"
}
Manage Issues
GET /api/crash/projects/:id/issues
List issues with filters Query Parameters:
status - Filter by status (unresolved, resolved, etc.)
sort - Sort by (last_seen, first_seen, event_count)
query - Search in title/message
GET /api/crash/projects/:id/issues/:issueId
Get issue detail with recent events Response: {
"issue" : {
"id" : "iss_xxx" ,
"short_id" : "PROJ-123" ,
"title" : "TypeError: Cannot read property 'x' of undefined" ,
"status" : "unresolved" ,
"event_count" : 42 ,
"user_count" : 15 ,
"first_seen" : "2026-01-01T00:00:00Z" ,
"last_seen" : "2026-01-18T12:00:00Z"
},
"events" : []
}
POST /api/crash/projects/:id/issues/:issueId/resolve
Mark issue as resolved Request: {
"resolved_in_release" : "1.2.4"
}
Upload Source Maps
POST /api/crash/projects/:id/artifacts
Upload source maps (multipart/form-data) Form fields:
release - Release version
dist - Distribution variant (optional)
Files: .map and .js files
Real-time Updates (SSE)
Subscribe to crash events:
const eventSource = new EventSource (
'https://loom.example.com/api/crash/projects/proj-123/stream' ,
{ headers: { Authorization: 'Bearer <token>' } }
);
eventSource . addEventListener ( 'issue.regressed' , ( event ) => {
const data = JSON . parse ( event . data );
console . log ( `Issue ${ data . short_id } regressed!` );
showNotification ( `Issue regressed: ${ data . title } ` );
});
Events:
crash.new - New crash event
issue.new - New issue created
issue.regressed - Resolved issue regressed
issue.resolved - Issue resolved
issue.assigned - Issue assigned to user
Stack Frames
Stack frames include source context after symbolication:
{
"function" : "handleSubmit" ,
"module" : "components/Form" ,
"filename" : "Form.tsx" ,
"lineno" : 42 ,
"colno" : 15 ,
"context_line" : " const result = await api.submit(data);" ,
"pre_context" : [
" async handleSubmit(data) {" ,
" try {" ,
" setLoading(true);"
],
"post_context" : [
" setSuccess(true);" ,
" } catch (error) {" ,
" setError(error);"
],
"in_app" : true
}
Best Practices
Upload Source Maps Always upload source maps for production builds to get readable stack traces
Set Release Version Use semantic versioning or commit SHA for release tracking
Add Breadcrumbs Log user actions before crashes to aid debugging
Filter Noise Use beforeSend hook to filter known browser quirks (ResizeObserver, etc.)
See Also
Analytics Link crashes to person profiles
Sessions Track crash-free session rates