The 2am Call That Started Everything
It was 2am on a Thursday. Our checkout page was crashing in production.
I opened Sentry. It showed me this:
TypeError: Cannot read properties of undefined (reading 'map')
at UserList (UserList.tsx:42)
at renderWithHooks (react-dom.development.js:14985)
Five engineers on a call. One shared their screen. One opened the logs. One blamed the last PR. Two were silently Googling.
Three hours later, we found it — a useEffect missing a cleanup function. The heap quietly climbed to 92%, the garbage collector gave up, and a perfectly healthy .map() became the scapegoat.
The stack trace knew exactly where it happened. It had absolutely no idea why.
The Gap Nobody Talks About
Here's the dirty secret of frontend error monitoring:
Every tool on the market does the same thing — captures the exception, groups it by stack trace, sends you a Slack alert. Then it's your problem.
You still have to figure out:
- Is this a race condition?
- A failed API call?
- A memory leak?
- A hydration mismatch?
You don't know. The tool doesn't know. Enjoy your next 3 hours of detective work.
I asked myself a simple question that night:
Why does every error tool tell me WHAT crashed, but none of them tell me WHY?
Introducing CrashSense
CrashSense is a crash diagnosis SDK — not another error tracker.
That same TypeError? CrashSense returns this:
{
category: "memory_issue",
subcategory: "memory_leak",
confidence: 0.87,
severity: "critical",
contributingFactors: [
{ factor: "high_memory_utilization", weight: 0.9,
evidence: "Heap at 92% (487MB / 528MB)" },
{ factor: "memory_growing_trend", weight: 0.8,
evidence: "Heap growing at 2.3MB/s over 60s" },
{ factor: "high_long_task_count", weight: 0.6,
evidence: "4 long tasks in last 30s (avg 340ms)" },
],
breadcrumbs: [
{ type: "navigation", message: "User navigated to /checkout" },
{ type: "click", message: "User clicked 'Add to Cart'" },
{ type: "network", message: "POST /api/cart → 200 (142ms)" },
{ type: "console", message: "Warning: memory pressure elevated" },
],
aiAnalysis: {
rootCause: "Memory leak in useEffect — event listener not cleaned up",
fix: { code: "return () => window.removeEventListener('resize', handler);" },
prevention: "Always return cleanup functions from useEffect with side effects",
}
}
Root cause classified. Evidence chain provided. Fix suggested.
The junior on call could've resolved it. At 2:01am. Without waking anyone up.
How It Actually Works — Under the Hood
CrashSense isn't magic. It's a classification engine that correlates system signals with error patterns in real-time.
1. Continuous System Monitoring
The SDK passively monitors 4 system dimensions using native browser APIs:
| Monitor | API Used | What It Tracks |
|---|---|---|
| Memory | performance.memory |
Heap usage, utilization %, growth trend |
| Event Loop |
PerformanceObserver (Long Task) |
Blocking tasks, frame timing |
| Network |
fetch/XHR interception |
Failed requests, timeouts, offline state |
| Iframe | MutationObserver |
Iframe count, cross-origin detection |
All monitoring is passive — no monkey-patching, no proxy wrapping. CPU overhead stays under 2%.
2. Multi-Signal Classification
When an error occurs, the classifier doesn't just look at the error message. It correlates the error with the current system state:
Error: TypeError (reading 'map')
+ Memory at 92% and growing → memory_issue signal
+ 4 long tasks in last 30s → event_loop signal
+ 0 network failures → not network_induced
+ React component stack present → check framework_react
Each signal gets a weight based on evidence strength. The category with the highest weighted score wins, and the confidence reflects how sure the system is.
7 crash categories:
| Category | What It Catches |
|---|---|
memory_issue |
Memory leaks, heap spikes, OOM |
event_loop_blocking |
Infinite loops, frozen UI, long tasks |
framework_react |
Hydration mismatches, infinite re-renders, hook violations |
framework_vue |
Reactivity loops, watcher cascades, lifecycle errors |
network_induced |
Offline crashes, CORS blocks, timeout cascades |
iframe_overload |
Excessive iframes exhausting memory |
runtime_error |
TypeError, ReferenceError, RangeError |
3. Breadcrumb Trail
Every user interaction is automatically recorded — clicks, navigation, network requests, console output, state changes. When the crash report lands, you're reading a timeline, not a stack trace.
4. AI Fix Suggestions (Optional)
Plug in your own LLM (OpenAI, Anthropic, Google, or any compatible endpoint). CrashSense sends a structured, token-optimized crash payload and returns parsed root cause analysis with fix code.
![Architecture diagram showing CrashSense monitoring pipeline: System Monitors → Event Bus → Classifier → Crash Report]

Setup in 60 Seconds
Core (Vanilla JS / Any Framework)
npm install @crashsense/core
import { createCrashSense } from '@crashsense/core';
const cs = createCrashSense({
appId: 'my-app',
onCrash: (report) => {
console.log(report.event.category); // "memory_issue"
console.log(report.event.confidence); // 0.87
console.log(report.event.contributingFactors);
},
});
That's it. Memory, event loop, network — all monitored. Every crash classified.
React — 3 Lines
npm install @crashsense/core @crashsense/react
import { CrashSenseProvider } from '@crashsense/react';
function App() {
return (
<CrashSenseProvider
config={{ appId: 'my-app' }}
onCrash={(report) => console.log(report)}
>
<YourApp />
</CrashSenseProvider>
);
}
Automatically catches: hydration mismatches, infinite re-render loops, hook ordering violations.
Hooks for granular control:
import { useCrashSense, useRenderTracker } from '@crashsense/react';
function Checkout() {
const { captureException, addBreadcrumb } = useCrashSense();
useRenderTracker('Checkout'); // warns on excessive re-renders
const handlePay = async () => {
addBreadcrumb({ type: 'click', message: 'User clicked pay' });
try { await processPayment(); }
catch (err) { captureException(err, { action: 'payment' }); }
};
return <button onClick={handlePay}>Pay Now</button>;
}
Vue — Plugin + Composables
npm install @crashsense/core @crashsense/vue
import { createApp } from 'vue';
import { crashSensePlugin } from '@crashsense/vue';
const app = createApp(App);
app.use(crashSensePlugin, { appId: 'my-app' });
app.mount('#app');
Catches: reactivity loops, lifecycle errors, component failures via app.config.errorHandler.
AI Integration — Bring Your Own LLM
npm install @crashsense/ai
import { createAIClient } from '@crashsense/ai';
const ai = createAIClient({
endpoint: 'https://api.openai.com/v1/chat/completions',
apiKey: process.env.OPENAI_API_KEY!,
model: 'gpt-4',
});
const analysis = await ai.analyze(report.event);
console.log(analysis?.rootCause); // "Memory leak in useEffect..."
console.log(analysis?.fix?.code); // "return () => window.removeEventListener(..."
Pre-Crash Warnings — Know Before It Dies
This is the feature I'm most proud of.
CrashSense doesn't just tell you after the crash. It detects dangerous conditions before the browser tab dies:
| Level | Memory Trigger | Meaning |
|---|---|---|
elevated |
> 70% heap | System under pressure |
critical |
> 85% heap | High risk — take action |
imminent |
> 95% heap | Crash likely seconds away |
const cs = createCrashSense({
appId: 'my-app',
enablePreCrashWarning: true,
enableMemoryMonitoring: true,
});
Warnings are recorded as breadcrumbs. When the crash happens, the report shows the full escalation path: elevated → critical → imminent → crash.
Your app knew it was about to die. Now you can act on that.
![Pre-crash warning escalation timeline showing elevated → critical → imminent → crash]

The Hard Constraints
Building a monitoring SDK sounds straightforward until you face the real constraints:
1. The SDK must never crash your app.
One unhandled exception in your monitoring code, and you've made the problem worse. Every CrashSense operation runs inside defensive error boundaries.
2. Bundle size matters.
Teams won't adopt an 80KB monitoring SDK. CrashSense core is ~7KB gzipped with zero runtime dependencies.
3. PII must die at the SDK level.
Not on your server. Not "eventually." Emails, IPs, auth tokens, and credit card numbers are auto-scrubbed before any data leaves the browser. GDPR compliance starts at the source.
4. False positives destroy trust.
Every classification has a confidence score (0.0–1.0) with evidence strings. If the system isn't sure, it says so.
| Package | Size | Dependencies |
|---|---|---|
@crashsense/core |
~7KB | 0 |
@crashsense/react |
~1.3KB | 0 (peer: react) |
@crashsense/vue |
~1.2KB | 0 (peer: vue) |
@crashsense/ai |
~3.1KB | 0 |
CrashSense vs. The Rest
| Capability | CrashSense | Sentry | LogRocket | Bugsnag |
|---|---|---|---|---|
| Root cause classification | ✅ 7 categories | ❌ Stack trace only | ❌ | ❌ |
| Memory leak detection | ✅ Trends + utilization | ❌ | ❌ | ❌ |
| Pre-crash warnings | ✅ 3-tier escalation | ❌ | ❌ | ❌ |
| AI fix suggestions | ✅ Bring your LLM | ❌ | ❌ | ❌ |
| PII auto-scrubbing | ✅ SDK-level | ⚠️ Server-side | ❌ | ⚠️ |
| Bundle size | ~7KB | ~30KB | ~80KB | ~15KB |
| Runtime dependencies | 0 | Multiple | Multiple | Multiple |
| Pricing | Free forever | Free tier | $99/mo | $59/mo |
What's Next
v1.1.0 is already out with OOM Recovery Detection — when the browser kills a tab due to memory exhaustion, CrashSense recovers the full crash context (checkpoints, breadcrumbs, pre-crash warnings) on the next page load using sessionStorage snapshots and a 6-signal analysis engine.
On the roadmap:
- Source map support for production stack traces
- Dashboard UI for crash analytics
- More framework adapters (Svelte, Solid)
- Webhook integrations (Slack, Discord, PagerDuty)
Try It
npm install @crashsense/core
Links:
- 🔗 GitHub — star it if it saves you debugging time
- 📖 Documentation — full API reference, guides, examples
- 📦 npm —
@crashsense/core,@crashsense/react,@crashsense/vue,@crashsense/ai
Free. MIT licensed. Zero vendor lock-in.
Built by Hoai Nho — a tech lead who got tired of being the detective at 2am.


Top comments (0)