Why We Built Our Own Error Tracking
This is a technical implementation note about error tracking in Curling IO v3. It is written for software engineers and operators, and goes deeper into Rust, SQLite, durable jobs, source maps, diagnostic safety, and LLM requests than our usual product posts.
A request returning HTTP 500 or a background job failing gives us an error message, but investigating it usually requires more: the producing commit, a source location, the failure chain, the operations that ran before the failure, and enough occurrences to see whether the inputs vary.
Repeated failures need separate handling. One defect inside a loop can produce thousands of reports. We need to retain the occurrence count without sending an alert or making an LLM request for each report.
We could have sent these errors to a hosted error-tracking service. We built a narrower system ourselves for three reasons: data sovereignty, direct integration with our application and operations pipelines, and control over which internal data leaves our infrastructure.
The implementation is split between Curling IO and our separate Operations application. Curling captures a bounded diagnostic envelope without waiting for another service. Operations imports and deduplicates it, sends the alert, asks a fast, lightweight, low-cost LLM for a structured analysis through OpenRouter when that integration is enabled, and stores the result with the issue. The redacted issue record is available through a command and a static report.