How to Fix GPT Routing Problems in Your Automation Workflows
GPT routing problems in n8n, Make and Zapier: dropped tickets, overlapping routes, no confidence, silent model changes. Symptom, cause and fix for each one.
Your workflow sends text to a GPT chat model and branches on the answer: which team, which queue, which follow-up. Most days it works. Then this happens:
Model output: "Billing Support"
Switch rule: equals "billing" → no match
equals "technical" → no match
equals "sales" → no match
Result: falls to the default branch, or is dropped
The ticket is not misrouted; it is un-routed, and unless someone watches the default branch, nobody knows. Multiply by the ways this shape of problem shows up (casing, synonyms, overlapping categories, silent model updates) and you have the daily experience of GPT routing problems in n8n, Make and Zapier.
This is a fix-it guide. Eight recurring GPT routing problems, each with symptom, cause, and a fix that starts on the chat side. The biggest fix is structural: stop using a chat model for the routing step and use a decision model instead, presented fairly below in both its hosted and self-hosted forms. For why chat-style classification fails, read the classification-failures guide; this piece stays at the workflow level.
The eight GPT routing problems
Problem 1: The answer doesn't match the Switch rules
Symptom: items land in the default branch or vanish; the log shows the model returned Billing Support, billing team, or BILLING.
Cause: the model generates plausible text; your Switch matches exact values. Casing, synonyms and extra words all break the match.
Fix: use structured outputs with an enum of route values where your provider supports it. Validate the answer before the Switch, and alert on the default branch instead of letting it swallow items silently.
Problem 2: No "none of these" route
Symptom: out-of-scope messages get forced into the nearest team and mis-handled. The customer asked about a product launch; the ticket went to sales by default.
Cause: the prompt offers no escape hatch, so the model must pick something.
Fix: add an explicit other or needs_review option and a branch that treats it as review, not failure.
Problem 3: Overlapping routes
Symptom: the same kind of message flips between two teams across runs. Billing vs. refunds is the classic pair: "I want my money back for a charge" lives in both.
Cause: the categories genuinely overlap as described. The model is arbitrating ambiguity you left in the definitions.
Fix: rewrite the option descriptions so a reasonable person could pick, merge or split the overlapping categories, and check the result against a test set. No model fixes ambiguous options for you.
Problem 4: Messages with more than one intent
Symptom: "I was charged twice and now I'm locked out" needs two teams; a single-label route picks one and drops the other half of the problem.
Cause: single-choice output by design.
Fix: ask for multi-label output and fan the item out to both branches.
Problem 5: Silent behaviour changes
Symptom: routing percentages shift with no error, after a prompt edit or a provider model update. Your dashboards are green; your routes are different.
Cause: chat models are versioned by the provider, and prompts are sensitive inputs. Neither change raises an exception.
Fix: pin model versions where the provider offers it, keep a labeled test set, and re-run it before and after any change. Treat routing like code: no change ships untested.
Problem 6: No confidence signal
Symptom: you cannot tell a clear-cut route from a coin flip, so everything auto-routes or nothing does.
Cause: chat answers arrive as committed text. A written-out confidence is not a number you can threshold on.
Fix: there is no complete fix on the chat side. This is the structural gap the bigger fix addresses.
Problem 7: Stacked calls, cost and latency
Symptom: route, priority and tags are three separate chat calls per item, each paying prompt-plus-output tokens, each adding latency in series.
Cause: one answer per completion.
Fix: consolidate prompts where you can, and move the decision-shaped steps off per-token billing.
Problem 8: Changing route lists
Symptom: adding a team means rewriting the prompt, re-testing everything, and hoping the new wording did not shift the old routes.
Cause: the route list lives inside prompt text.
Fix: keep the route list as data passed into each call, so edits are data edits, not prompt surgery.
The bigger fix for GPT routing problems: a decision model for the routing step
A decision model does not write an answer. You pass the situation and the options; it returns a probability for each. For routing, that addresses by design:
- Problem 1 (off-list): it can only score the options you passed.
- Problem 6 (confidence): every option comes back with a probability you can threshold on, plus a margin between the top two.
- Problem 7 (stacked calls): route, priority and tags are typed questions in one request.
- Problem 8 (changing lists): the options travel with every request, as data.
It does not fix Problem 3 (overlapping categories are still your definitions), does not remove the need for a test set and pinned versions (Problem 5), and it can still pick the wrong route.
Hosted: GPT-6 Luna Decisions
Released by OpenAI on 2026-10-06: a Decisions API that returns probabilities for each option, callable from n8n through an HTTP Request node. specs in GPT-6 Luna Decisions alternatives The request is a JSON body with the input and your questions, roughly this shape (illustrative; field names and endpoint are in your provider's docs):
POST <decisions endpoint>
Authorization: Bearer $API_KEY
{
"state": "<ticket text>",
"questions": [
{"id": "route", "type": "choice", "options": ["billing", "technical", "other"]}
]
}
Check your provider's docs for the exact request shape, current limits and how multi-intent tagging is handled.
Self-hosted: Saina Helm
An open-weights decision model on your own servers: text never leaves your infrastructure, there is no per-token bill, and multi-label questions are native. Self-host via Docker (ghcr.io/run-saina/saina-server:0.1.2) or pip install 'saina[local,server]'. A routing call against its /v1/ask endpoint:
from saina import Saina
client = Saina('https://helm.internal.example', api_key='YOUR_KEY')
result = client.ask(
model='saina-helm-0.8b',
state=ticket_text,
mode='decision',
threshold=0.8,
questions={'route': {
'type': 'single_choice',
'question': 'Which team should handle this?',
'options': {'billing': 'Charges, invoices, refunds',
'technical': 'Outages, errors, account access',
'other': 'Anything else'},
}},
)
route = result['answers']['route']['selection'] # None when rejected; see ['reason']
The n8n community node (self-hosted n8n, npm package @run-saina/n8n-nodes-saina) exposes a Selected output (accepted answers) and a Fallback output (below threshold, below the minimum margin, tie, and inference errors only if you set error handling to use Fallback). The support-routing.json template in the n8n-nodes repo wires billing/technical/other with a fallback branch; on n8n Cloud, use the HTTP Request template. Trade-offs: you run and maintain a server. Full specs are in the LLM decision making pillar.
The two compared
| GPT-6 Luna Decisions | Saina Helm | |
|---|---|---|
| Hosting | OpenAI | Your servers |
| Data location | Provider | Your infrastructure |
| Pricing model | Per input token | Fixed self-hosting cost |
| Listed latency | ~0.2 s median (OpenRouter, 2026-10-07) | 0.9–2.7 s on CPU (measured, 2026-10-06) |
| Question types | yes/no, choice, score | yes/no, single-choice, multi-label, rating |
| Max questions per call | 200 | 256 |
| Multi-label | One yes/no per tag | Native |
| n8n | HTTP Request node | Community node (self-hosted) / HTTP template (Cloud) |
| Version control | Provider-managed | You pin the weights revision |
Choose Luna Decisions if you want the easiest hosted option and already use OpenAI. Choose Helm if data must stay in-house, you need a fixed cost at high volume or offline operation, or you want the n8n node's built-in Fallback branch. Either way: run it in shadow mode against your current routing before switching anything live.
Quick reference
| # | Problem | Chat-side fix | Decision model? |
|---|---|---|---|
| 1 | Answer off the route list | Structured outputs + enum | Yes, by design |
| 2 | No "other" route | Add the option + branch | You still add the option |
| 3 | Overlapping routes | Rewrite/merge categories | No |
| 4 | Multi-intent messages | Multi-label output | Yes (native for Helm) |
| 5 | Silent changes | Pin versions + test set | Pinning helps; test set still yours |
| 6 | No confidence | Incomplete on chat side | Yes, probabilities |
| 7 | Stacked calls | Consolidate prompts | Yes, one call |
| 8 | Changing route lists | Routes as data | Yes, options per request |
Routing workflow checklist
- The Switch (or IF chain) has a default branch, and something alerts when items land there.
- There is an explicit
other/needs_reviewroute that goes to a person, not to a queue nobody reads. - The answer is checked against the allowed route values before the Switch.
- Multi-intent messages fan out to every matching branch, not just the first.
- The route list lives in one place as data, not copied across prompts.
- Low-confidence routes go to a review branch (a Fallback output, or rejection sampling on the chat side).
- Any new routing step runs in shadow mode against current routing on 50–100 labeled tickets before it goes live.
Related posts
- LLM classification failures (why chat-style classification breaks)
- LLM decision making (pillar)