How much of your AI workflow is real work versus cleaning up after the model?

:waving_hand: Hey n8n Community,

I am building a lot of flows at the moment that make LLM calls somewhere in the middle, and the thing that keeps getting in my way is the output. You ask for JSON, and what comes back is usually close to JSON but not quite. After talking to a lot of other builders in the community, I realized this is something pretty much everyone runs into.

One run arrives wrapped in triple backticks. Another starts with “I’ll help you with that” before the object, so the parse dies on the first character. Sometimes the output key sits nested inside another output key. Sometimes the field is called output, sometimes text, sometimes response. A missing value shows up as an empty string, or null, or the literal text “null”.

And the usual safeguards do not fully solve it. We run the structured output parser with auto fixing and retry on fail and still hit intermittent format failures, and n8n’s own docs admit parsing is often unreliable with agents, suggesting you pipe the output into a second LLM chain just to clean up the first one.

So the chain grows. Strip the fences, parse, normalize types, check the fields that have to be there, retry when the shape is wrong. What helps me most is telling the model to return null when a value is absent and having one isMissing helper that catches real null, undefined, empty string, whitespace and the string “null”, but it is still four nodes of plumbing around one actual operation.

That is what my friends and I have been working on: you set up the AI task and its schema once on a platform outside n8n, then call it from a single node, and the output comes back in that shape every time with nothing to parse behind it.

Mostly I wanted to hear from the community whether something like this actually makes sense, and whether it would solve a real problem for other builders or if I am overthinking a problem that most of you have already worked around. If you would want to help shape it, feel free to reach out and I am happy to talk.

How much of your AI setup is actual work versus cleanup after the model?

Best,
Felix

Hi Felix, you’re not overthinking this — that cleanup layer is real work, because the model returns text that only looks like JSON.

The pattern that keeps it manageable inside n8n, without adding a second system, is to make cleanup a reusable step instead of repeating it in every workflow:

  • Strip the fences and leading chatter before parsing, then extract the first JSON object or array rather than parsing the whole reply.
  • Keep the Structured Output Parser, but treat a failed parse as a retry with a repair instruction, not as a reason to send the output to a second LLM by default.
  • Normalize once: turn an empty string, whitespace, and the literal text “null” into a real null, and check required fields before the workflow continues. Your isMissing idea is the right shape for that check.
  • Put that strip, parse, and normalize sequence in one sub-workflow and call it from each AI step, so the schema is defined once and the four nodes of plumbing do not multiply across workflows.

A single-node external platform is appealing, but it also adds another place where the schema lives, another service to debug, and data leaving n8n. I would try the reusable sub-workflow version first, and only move it out if the same failures keep showing up after that cleanup step is in place.

How are you handling the cases where the JSON is valid but a field has the wrong type — do you reject and retry, or coerce it and move on?

This could cause n8n’s JSON Parser to fail.

Felix’s 5 main problems:

  1. AI sends extra text along with JSON.
  2. Triple backticks end up inside the JSON.
  3. Output sometimes comes as output, sometimes as text, sometimes as response.
  4. Missing values are sometimes null, sometimes empty strings.
  5. Structured Output Parser also fails sometimes.

2. How do we solve this problem in n8n?

My recommendation is to build a structured output workflow.

**Recommended n8n workflow** **Input / Trigger** Will receive user data **Basic LLM Chain** Will generate structured output from AI **Structured Output Parser** Will validate output according to JSON schema **Code Node** Will normalize null values, empty strings, and output **Validated JSON Output** Next workflow nodes will receive clean data

Important: Instead of using an AI Agent, you should also test the combination of Basic LLM Chain and Structured Output Parser for strict structured output. The Code Node will help with normalization, but it cannot guarantee to automatically repair malformed JSON in every situation.

3. What should you reply to Felix?

My recommendation is that you don’t just send him a generic message. Share your technical solution with him and ask about his proposed platform.

Use this professional reply:

Hi Felix,

Thanks for sharing this. I think this is a genuine problem that many n8n automation developers face when working with LLMs.

I believe this issue can be addressed with a structured output pipeline in n8n.

My suggested approach would be:

  1. Use a Basic LLM Chain with a clearly defined JSON schema.
  2. Use a Structured Output Parser to validate the expected structure.
  3. Add a Code node to normalize inconsistent values such as null, undefined, empty strings, whitespace, and the literal string “null”.
  4. Handle different output wrappers such as output, text, and response.
  5. Add error handling and a retry mechanism when the output does not match the expected schema.

I would also recommend using the model’s native JSON mode when supported.

One important point is that validation and normalization should be separate steps. This makes the workflow easier to debug and maintain.

Your idea of handling this through a single node is interesting because it could reduce unnecessary workflow complexity.

I have a few questions:

  • Does your platform support custom JSON schemas?
  • Does it validate the output before returning it to n8n?
  • Can it handle missing fields and different output structures?
  • Do you have a working demo or an n8n integration available?

I would be interested in learning more and exploring how this could improve real-world n8n workflows.

Best regards

4. How to post a reply in the n8n Community?

Open Felix's original post in the n8n Community. Click the **Reply** button below the post. Copy the English message provided above. Paste it in the reply box. Review the message once and press the **Reply** / **Post** button.

5. What do your Think AI Works internship emails mean?

You’ve been given 5 projects:

Project Deadline
Recruitment Pipeline 4 November
Telegram Expense Bot 9 November
FeedbackLoop 14 November
AssetTracker 19 November
PetCare Hub 24 November

Important points for you:

  • You don’t need to buy paid tools.
  • You can use n8n Community Edition.
  • You can use free options like Ollama, Gemini, or Groq for AI.
  • You need to create a GitHub repository for each project.
  • You need to submit a 60–90 second demo video for each project.
  • You need to send a progress email every night at 9 PM PKT.

Important: Today is 31 October and according to the email, you were supposed to send an update by 9 PM tonight. If you haven’t sent it yet, send it now.

My advice: Focus on the Recruitment Pipeline first, since its deadline is 4 November. Keep Felix’s Community post separate from your internship project.

6. We can now start with practical work

I can teach you how to build an actual n8n workflow for Felix’s problem, which will:

  • Generate JSON from AI.
  • Automatically validate the JSON.
  • Normalize null, undefined, and empty values.
  • Have error handling for incorrect output.
  • Deliver final clean JSON.

This way, you can not only give Felix a suggestion but also show him a working demonstration.

This is what I call the “LLM Janitor Tax” and it’s easily the #1 reason AI workflows fail when moving from a fun demo into production.

The core problem is that people rely on prompt engineering (“Please output strictly valid JSON, do not include markdown or conversational filler”) to enforce data structures. But probabilistic models will always eventually hallucinate markdown backticks (```json), trailing commas, or conversational preambles (“Here is the JSON you requested:”).

To get to 99.9% reliability without spending hours writing cleanup logic, here is the 3-tier pattern we use in production n8n workflows:

i Enforce Structured Outputs at the API Engine (Not the Prompt)
If you are using the AI Agent or Basic LLM Chain node in n8n, never ask for JSON in the prompt alone:

Connect the Structured Output Parser sub-node directly to the Model / Chain.
Define your schema using JSON Schema.
Under the Model node options, toggle Response Format to JSON (or strict structured outputs if using OpenAI/Gemini).
When you enforce it at the API parameter level, the model’s token sampling logits are constrained, making it mathematically impossible for the LLM to output non-JSON characters or conversational filler.

ii. The Universal “Markdown & Preamble” Sanitizer (Code Node)
If you are calling models via the HTTP Request node or using providers that occasionally leak markdown formatting, drop this single Code node immediately after your LLM call.

It strips markdown code fences and uses a greedy regex anchor to extract only the valid JSON payload, discarding any conversational preamble before it:

javascript

// Sanitizes LLM response: strips conversational preambles and markdown fences
for (const item of $input.all()) {
let raw = item.json.text || item.json.content || item.json.message?.content || “”;

// 1. Remove markdown backticks (```json and ```)
let cleaned = raw.replace(/```(?:json)?/gi, “”).replace(/```/g, “”).trim();

// 2. Greedy match the first outer JSON object {…} or array […]
const jsonMatch = cleaned.match(/(\{[\s\S]*\}|$[\s\S]*$)/);

if (jsonMatch) {
try {
item.json = JSON.parse(jsonMatch[0]);
} catch (err) {
item.json = { parse_error: true, raw_output: raw, error: err.message };
}
} else {
item.json = { parse_error: true, raw_output: raw };
}
}

return $input.all();

iii. The “Self-Healing JSON” Failover Branch
For mission-critical workflows where broken JSON would halt a business process:

After the Code node above, add an If node checking if parse_error is true.
On the True branch (rare edge cases where the LLM generated truncated or invalid JSON with missing brackets):Route the payload to an ultra-fast, cheap model (like gpt-4o-mini or gemini-1.5-flash).
Prompt: “The following string was supposed to be valid JSON but contains syntax errors. Fix the formatting and return only the raw JSON object:”
Re-parse and merge back into the main pipeline.
This eliminates 100% of human intervention. You stop cleaning up after the model, and the pipeline becomes self-healing.

Curious to hear: are you mostly hitting this on standard chat models, or inside custom function-calling / tool nodes?

Good points from both of you — and Livinus, that ‘LLM Janitor Tax’ name nails it.

Sardar, your pipeline is the same shape I was describing, just with the Code node doing the normalizing — that works. On your questions: I define the schema once in the Structured Output Parser and reuse it through the sub-workflow, so every AI step validates against the same shape instead of each prompt inventing its own. The required-field check runs right after normalize, before the data goes anywhere else — if a field is missing or the wrong type, it goes back for a repair retry with the specific failure named, rather than flowing downstream half-formed. If the repair retry fails twice, it stops and flags the item instead of guessing. I don’t have a public demo of this exact setup to share, but the shape is simple: AI step, then strip/parse/normalize, then the required-field check, and only then the next node.

Livinus, on your question: this hits hardest on standard chat models, where the model is free to add chatter around the JSON. With strict structured output enforced at the API level the fences mostly disappear, but I would still keep the normalize step — a model can return perfectly valid JSON with a null where you expected a string.

Use the model’s native structured output or tool calling with a schema instead of asking for JSON in the prompt. For anything that still slips through, add a Code node that trims everything outside the outermost curly braces and maps output, text and response onto one key. Validate before the next node runs.