Field SOP
Field SOP

Bug Triage Automation Workflow: Route Reports the Moment They Arrive

An n8n workflow for bug triage automation: Webhook receives bug report -> extract fields -> LLM classification (severity P0-P3 + module + repro) -> IF P0/P1 notify on-call and create high-priority issue, else route by module -> auto-reply to reporter. Includes a .json template.

Published August 2, 20265 min read
<!-- workflow-bug-triage-automation | resource | Bug Triage Automation Workflow: Route Reports the Moment They Arrive -->

Where do bug reports come from? A user saying "it crashed" in the community, a support ticket with repro steps, a screenshot a tester dropped in Slack, an auto-generated ticket from monitoring, a form submitted by an external user-scattered across five or six entry points. Who triages them? Manually reading and prioritizing works at ten a day, but past thirty the cracks show: a P0 sits buried at the bottom of the queue, the same crash gets reported three times by three people with no merge, a report missing repro steps stalls while someone chases the reporter for info. Worse, "who owns this" is a back-and-forth: frontend or backend? Payments or auth? Can you reproduce it? By the time that's sorted, production has been burning for half an hour.

This n8n workflow unifies multi-source bug reports into one pipe, auto-extracts fields, runs an LLM to classify severity (P0-P3) + module + reproducibility, pushes P0/P1 straight to on-call and files a high-priority issue, routes the rest by module to the right owner queue, then auto-replies to the submitter with "received + expected handling" and writes back status. A bug report goes from submission to dispatch in minutes-urgent bugs no longer get found by whoever shouts loudest.

Workflow Chain

Webhook trigger (bug report form / email relay / IM bot / monitoring alert forward) -> Code node extracts fields (title / description / environment / repro_steps / reporter, normalizing multi-source schema) -> LLM classification (severity P0-P3 + module + reproducible + missing_info detection) -> IF urgent (P0/P1) -> notify on-call immediately (Slack / Feishu / phone webhook) + create high-priority issue (HTTP Request to issue tracker) -> otherwise Switch routes by module to the right owner queue -> auto-reply to submitter (received + expected handling time) -> status writeback.

The core difference from the existing GitHub Issue Auto-Triage workflow: that one handles a single source (GitHub issues) and writes labels back to GitHub. This workflow handles multi-source bug reports (forms / email / IM / monitoring), adds field extraction, reproducibility assessment, module-based routing, and submitter auto-reply-doing end-to-end "report in, dispatched to a person" triage.

Download Template

Setup Steps

  1. Import: n8n -> Workflows -> Import from File, pick workflow-bug-triage-automation.json
  2. Webhook node: copy the Production URL, wire it to your bug-report submission entry. The key for multi-source intake is field unification: form submissions usually arrive as structured JSON (title/description/environment/steps all present), email relays give subject + body text, IM bot forwards might be a screenshot + one line. Regardless of source, everything hits the Webhook and the Code node normalizes it
  3. Extract fields node (Code): normalizes the multi-source payload into a unified schema-title, description, environment, reproSteps, reporter, reporterContact, source. Email input takes title from subject, description from body; form input maps directly. Also hashes title + environment for dedup-three people reporting the same crash counts as one but tracks duplicate count
  4. LLM classify node: fill in Kimi / DeepSeek / Qwen key; 8k context is enough. Prompt below; the model outputs severity (P0-P3 enum) + module + reproducible + missing_info. missing_info is key: if the report lacks repro steps or environment, the model flags it and the auto-reply asks for it
  5. IF urgent node: severity == P0 or P1 takes the urgent branch
  6. Notify on-call node: Slack / Feishu / DingTalk group-bot webhook, message carries severity + module + summary + reporter + source so on-call can judge at a glance. For P0, pair with phone alerting (PagerDuty / Opsgenie webhook)-IM-only at 3 AM equals no notification
  7. Create high-priority issue node: HTTP Request to your issue tracker (GitHub / GitLab / Jira) API, title prefixed with [severity], labels tagged bug + auto-triaged + module + priority. Body is structured with description / environment / repro steps / missing info so the developer can dig in immediately
  8. Route by module node (Switch): P2/P3 are not urgent; route by module to the right owner. The template ships frontend / backend example branches + a default queue; add branches to match your team-infra / data / mobile / api. Each branch feeds an HTTP Request notifying the relevant Slack channel or Feishu group
  9. Auto-reply to submitter node: HTTP Request to your reply endpoint (email API / form writeback / IM bot DM), content is "received + ticket ID + severity + expected handling time." Expected handling maps from severity: P0 immediate, P1 same day, P2 this week, P3 next sprint. If missing_info is non-empty, the reply asks "please provide: repro steps / browser version / account ID"
  10. Status writeback node: HTTP Request PATCH to your bug-record system, marking status=triaged + severity + module + triaged_at. This is the closure of the loop-after submission, status moves from new to triaged and both the submitter and the team can look up the dispatch result
  11. Test run: manually POST one P0 bug (with repro steps) + one P2 bug (missing environment), check whether on-call got the urgent notification, whether the issue tracker created a high-priority issue, whether the P2 routed to the module owner, whether the submitter got an auto-reply, and whether status was written back

Companion Prompt (LLM Bug Triage Classification)

Prompt
You are a bug triage engineer. Read the bug report below and output JSON:
- severity: P0 (crash / data loss / security vulnerability / large-scale outage) / P1 (core feature blocked) / P2 (minor feature degraded) / P3 (cosmetic / UX issue)
- module: frontend / backend / infra / unknown (pick one of four, inferred from description)
- reproducible: yes (clear repro steps given) / no (clearly cannot reproduce) / unknown (insufficient info to judge)
- confidence: 0-1
- summary: one sentence within 30 words
- missing_info: array of missing critical info (e.g., if repro steps are empty add "repro_steps", if environment is empty add "environment"; empty array means info is complete)
Rules: severity must come from the four given levels; do not fabricate facts not in the report; judge only from the original text; when info is too scarce to determine severity, default to P2 and prefix summary with [insufficient info].
Bug title: {{title}}
Bug description: {{description}}
Environment: {{environment}}
Repro steps: {{reproSteps}}

Advanced

  • SLA timer: after status writeback, add a Schedule + Code node that sweeps unclosed bugs every hour; when SLA threshold is exceeded (P0 1 hour, P1 8 hours, P2 3 days, P3 1 sprint), auto-escalate. SLA breaches are more alarming than "new bug arrived"-the former means something slipped, the latter is normal flow
  • Dedup: the Code node already hashes title + environment, but the same bug described differently by different people won't match. The advanced move: on hash hit, append the new report as a comment on the existing issue (GitHub Issues API POST /repos/.../issues/:number/comments), don't create a duplicate issue but keep all reporter info; when the issue closes, notify all reporters at once
  • Oncall schedule integration: don't hardcode the webhook URL in the notify-on-call node. Instead, first HTTP Request your oncall schedule system (PagerDuty / Opsgenie / a self-built rotation table), fetch the current on-call, then push to that person's channel. Without this, a 3 AM P0 goes to last week's on-call-effectively a no-op
  • Trend stats: all triaged bugs go into a summary table (Feishu Bitable / Notion / Postgres); weekly stats by severity + module. "P0/P1 count week-over-week" and "which module has the highest bug density" drive quality improvements far better than "total bugs this week"
  • Confidence tiering: when LLM confidence < 0.7, don't auto-create the issue-route to a human-review queue. Accumulated human labels can fine-tune a dedicated small model to replace the general LLM, better on cost and stability

Pitfalls

  • Pin severity definitions in the prompt: don't just say "judge severity"-P0/P1/P2/P3 definitions must be spelled out (crash=P0, core blocked=P1) or the model will flag "wrong button color" as P1. Tie definitions to your team SLA; when they change, record prompt_version in the summary table
  • missing_info is triage quality: reports missing repro steps are the norm, not the exception. When the LLM detects missing_info, asking in the auto-reply is far more effective than tagging the issue "needs info"-the former proactively reaches the submitter, the latter requires the submitter to check issue status themselves
  • Don't skip phone alerting: P0 with IM-only notification at 3 AM goes unread. The notify-on-call node for P0 should be dual-channel: IM push + phone alert (PagerDuty / Opsgenie) with an escalation policy that bumps to the next on-call if no response in 5 minutes
  • Don't over-fan module routing: frontend / backend / infra covers 90% of cases. Splitting into React / Vue / Node / Go / K8s / DB / Cache makes the Switch node expensive to maintain and the model bounces on edge cases. Start with three coarse branches and drill down as needed
  • Don't over-promise in auto-reply: "received + expected handling time" is enough; don't say "we're fixing it now"-P2/P3 might not be scheduled for two weeks, over-promising invites "is it fixed yet?" follow-ups. Include the bug ID so the submitter can self-serve status
  • Don't break the loop at dispatch: dispatched to a person is not the same as handled. After status writeback marks triaged, someone needs to claim it (assigned) before work starts. Add an assigned_at column in the summary table; if triaged but not assigned within X hours, auto-escalate-triaged with no owner is worse than not triaging at all

References

This article is AI-assisted and human-edited. Last updated: 2026-08-02

Related