AI Error Summarization for Node.js APIs
When an Express API throws in production, the stack trace lands in a log file and that's usually it until someone goes looking for it. I started piping error traces through an LLM to get a plain-English summary posted straight into my team's Slack channel before anyone even opens the logs. This is AI error summarization, and it's one of the more genuinely useful AI integrations I've shipped into a backend this year — not because it's clever, but because it shaves ten minutes off every incident response.
Why Error Summarization Beats Raw Stack Traces
A raw stack trace tells you where something broke. It doesn't tell you why, and it definitely doesn't tell you what to check first. On a client project last year, our on-call engineer would get paged, open the logs, stare at a 40-line trace through three layers of middleware, and spend the first five minutes just figuring out which line actually mattered.
Feeding that same trace to an LLM and asking "what's the likely root cause and what should I check first" turns that five minutes into about fifteen seconds of reading. The model isn't debugging for you — it's doing the triage pass a senior engineer does automatically and a junior one hasn't learned yet.
The Wrong Way: Blocking the Response
The first version I wrote called the OpenAI API directly inside the Express error handler, awaited the response, and only then sent a reply to the client. It worked in testing. It fell apart the moment the OpenAI API had a slow day.
// errorHandler.js — don't do this
app.use(async (err, req, res, next) => {
const summary = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: 'Summarize this Node.js error for an on-call engineer.' },
{ role: 'user', content: err.stack }
]
});
await slack.chat.postMessage({
channel: '#incidents',
text: summary.choices[0].message.content
});
res.status(500).json({ error: 'Internal server error' });
});
This holds the client's connection open for however long the OpenAI call takes — I've seen that spike past 4 seconds on a bad day. An error-handling path is exactly the wrong place to add a dependency on a third-party API's latency, and if OpenAI is down, your error handler itself throws, which is about as bad as it gets.
The Actual Fix: Background Processing
The fix for AI error summarization in any Express app is the same: respond to the client immediately and do the summarization and Slack post as a fire-and-forget background job. I use a lightweight queue for this (BullMQ backed by Redis), but honestly for a low-traffic internal tool, even setImmediate with a retry wrapper is fine — the point isn't the queue technology, it's decoupling the summarization from the response cycle.
// errorHandler.js — respond first, summarize after
app.use((err, req, res, next) => {
res.status(500).json({ error: 'Internal server error' });
errorSummaryQueue.add('summarize', {
stack: err.stack,
route: req.originalUrl,
requestId: req.id
}).catch(queueErr => {
console.error('Failed to enqueue error summary job', queueErr);
});
});
// worker.js
errorSummaryQueue.process('summarize', async (job) => {
const { stack, route, requestId } = job.data;
const summary = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: 'You summarize Node.js production errors for an on-call engineer. Be specific about the likely cause and the first file/line to check. Two sentences max.' },
{ role: 'user', content: `Route: ${route}\nRequest ID: ${requestId}\n\n${stack}` }
]
});
await slack.chat.postMessage({
channel: '#incidents',
text: `🔴 ${route} — ${summary.choices[0].message.content}`
});
});
Response time for the client goes back to normal, and if OpenAI or Slack has an outage, the worst case is a missed summary — not a failed request on top of the original error.
Getting the Prompt Right
I'd skip feeding the model the entire stack trace with every node_modules frame intact — most of those frames are noise and they eat into the context the model should be spending on your actual code. I trim anything from node_modules before sending it, and I explicitly tell the model to be specific about "the first file/line to check" rather than asking for a general summary. A vague prompt gets you a vague summary that just restates the error message in different words, which helps nobody.
The other thing worth doing: include the route and a request ID in the prompt, not just the stack. Half the time the actual root cause is "this route doesn't validate its input," and the model can only catch that if it knows what endpoint got hit.
Posting to Slack Without Spamming the Channel
If the same error fires fifty times in two minutes because a downstream dependency is flapping, you do not want fifty Slack messages and fifty OpenAI calls. I added a Redis-backed dedup check keyed on a hash of the error message and route, with a five-minute TTL, before the job even reaches the summarization step:
- Hash
${route}:${err.message}and check it against Redis with anEXPIREof 300 seconds - If the key exists, increment a counter on it and skip the LLM call entirely
- Every five minutes, if the counter is above zero, post one rollup message instead of nothing
That one change cut our OpenAI spend on this feature by more than half, and it meant the incident channel stayed readable during an actual outage instead of getting buried.
Key Takeaway
The single thing to get right here isn't the prompt or the model choice — it's keeping the LLM call off the response path entirely. Any AI error summarization setup that touches an error handler, a webhook, or any latency-sensitive route should treat the AI call the same way you'd treat a third-party API you don't fully trust: background it, queue it, and let the main request path finish without waiting on it.
Related posts
Fix OpenAI API Rate Limit Errors in Node.js
Get 429 rate_limit_exceeded errors from the OpenAI API in Node.js? Here is the real fix: exponential backoff, retry-after handling, and concurrency limits.
Caching OpenAI API Responses in Node.js
Caching OpenAI API responses in Node.js with Redis: exact-match vs semantic caching, the real tradeoffs, and code that actually cuts your costs.
Fixing OpenAI API Timeouts in Laravel Jobs
OpenAI API calls timing out inside your Laravel queue? Here's why jobs fail mid-request and how to set timeouts that actually agree with each other.
Stream OpenAI API Responses in Node.js with SSE
Learn how to stream OpenAI API responses in Node.js using Server-Sent Events, with real Express code and the mistakes that break streaming in production.
0 Comments
No comments yet — be the first to share your thoughts.