AI Error Summarization for Node.js APIs

Published · 1 views
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 an EXPIRE of 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.

#nodejs #openai-api #error-handling #ai-integration #bullmq #slack-api
Aliyan Faisal

Written by

Aliyan Faisal

Software engineer and AI developer. I build LLM-powered products, RAG pipelines and automations, and the web apps behind them.

0 Comments

No comments yet — be the first to share your thoughts.

Leave a comment

Never published.