DEV Community

Tarek Elzoghby | طارق الزغبي
Tarek Elzoghby | طارق الزغبي

Posted on Originally published at tarek-elzoghby.hashnode.dev

Ambiguous, Invalid, or System Error?

In my previous post, I wrote about the problems I found while defining the categories for an AI intake classifier. I left ambiguous and invalid for another post.

Those two states turned out to be only part of the problem. While wiring the workflow, I also needed to distinguish a request the model could not classify from a request the model never managed to evaluate.

This is the same practice project for a fictional automation agency, built in n8n with an AI collaborator. It is not a production client system. The workflow and project notes are in the GitHub repository.

Three different reasons for not choosing a project category

The classifier had three normal project categories:

  • simple_automation
  • custom_integration
  • ai_workflow

But not every submission should be forced into one of them.

ambiguous means the request is usable, but the text supports more than one category. The workflow should not pretend that a guess is a confident classification. In my sharper overlap test, I deliberately included concrete signals for both ai_workflow and custom_integration. The classifier returned ambiguous.

invalid means the input does not provide enough usable project intent to assign a normal category. This does not necessarily mean the person has no genuine request. A vague but relevant message can still contain partial intent. On the other hand, an out-of-scope message may contain nothing useful to route.

system_error means classification did not complete. The workflow did not reach a decision about the request, so treating it as invalid would report something the system did not know.

The difference matters because each state calls for a different response. A real request with overlapping categories may need a person to decide. An out-of-scope submission may not need a client email. A system failure needs troubleshooting or a retry, not a judgment about the request itself.

invalid needed one more field

My first definition of invalid treated missing information as the main signal. That overlapped with ambiguous, because both could describe a submission that could not be assigned cleanly. I changed the distinction: ambiguous is for multiple categories supported by the text; invalid is for input that does not support a responsible category assignment.

Then I noticed that one invalid label was still hiding two different situations:

  • The person has a real project in mind, but the request is too vague to classify yet.
  • The submission is unrelated or out of scope, with no useful project intent to follow up on.

I could have tried routing by keywords in the model's reasoning text, but that would make the workflow depend on the exact wording of an explanation. I also considered a conditional schema. Because the result passed through n8n's Structured Output Parser, I did not want to assume that conditional-schema behavior would be preserved through its translation layer.

I chose a simpler, always-present boolean: has_partial_intent.

{
  "category": "invalid",
  "has_partial_intent": true
}
Enter fullscreen mode Exit fullscreen mode

That keeps invalid as the category and makes the next decision explicit. It is easier for a downstream node to route on a boolean than to interpret free-text reasoning.

I tested it with three small examples:

  • help automating my workflow returned has_partial_intent: true. The request was vague, but it did point to a real automation need.
  • fix my car returned false. It was a request, but not one this intake workflow was built to handle.
  • I love football also returned false as unrelated input.

These examples helped check the distinction I had defined. They are not a representative dataset, and they do not tell me how often the classifier would get this right with real submissions.

A model failure is not an invalid submission

I also needed system_error for cases where classification itself failed. A network interruption or a broken model credential should not make the workflow label a valid request as invalid. The system has not made a decision about the content at all.

I tested the failure path by temporarily breaking the Gemini credentials. The workflow retried classification three times at five-second intervals, then reached the fallback route for human review. I also checked that the original submission data remained available on that path.

The ambiguous and system_error routes shared some downstream handling, but I kept the category values separate. The reason for sending something to human review is important: in one case the model made a decision that needs human judgment; in the other, it did not complete the decision.

That distinction affected the client-facing message too. For a system failure, it would have been misleading to send the same confirmation as if a normal classification had succeeded. The workflow needed to acknowledge the issue without claiming the request was invalid.

Why I used explicit routing

I had considered using an AI Agent to choose how these outcomes should be handled. I did not need another model-driven decision, though. The possible states were already known, and the workflow's job was to route each one consistently.

I used explicit Switch and IF branches instead. That made the expected paths visible in the workflow and easier to test. For the invalid case with has_partial_intent: false, I confirmed that no email was sent while the submission was still logged to Google Sheets. Logging did not depend on sending a client message.

What I can claim from these tests

I exercised a deliberately ambiguous request, vague and out-of-scope inputs, and a forced model failure. I also checked the fallback path and verified that the invalid/no-partial-intent case was logged without triggering emails.

That is evidence that these particular paths were exercised in this build. It is not proof of general classifier accuracy or production reliability. The examples were written by me, and a larger, independently labelled set of realistic submissions would be needed to measure that properly.

The useful lesson for me was that a fallback is not just a place to send everything that does not match a normal category. The workflow needs to preserve why it did not choose a category: competing interpretations, insufficient or irrelevant intent, or a failure to classify at all.


Disclosure: I used AI assistance while editing this article. The workflow design, tests, and conclusions describe my own build and observations.

Top comments (0)