Learn how to fix the "JSON parse error" in n8n caused by malformed AI model output by adding a Function node to detect, repair, and validate JSON for smooth workflow execution.
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
To fix the "JSON parse error" in n8n that occurs after malformed model output, you need to add a Function node that can handle and repair the broken JSON. This typically happens when using AI models that sometimes produce incomplete or malformed JSON. You can implement error handling that detects and corrects the JSON structure before passing it to other nodes.
Understanding the JSON Parse Error in n8n
The "JSON parse error" in n8n typically occurs when a node receives data that it expects to be valid JSON, but the data is malformed. This is particularly common when working with AI model outputs like those from OpenAI, which might sometimes generate incomplete JSON responses.
When n8n tries to parse this malformed JSON, it throws an error and halts your workflow execution. This can be frustrating, especially when you're working with large language models that may occasionally produce imperfect outputs.
Step 1: Identify the Source of the Error
First, let's identify where the JSON parse error is occurring:
Common issues include:
Step 2: Add a Function Node to Fix Malformed JSON
The most reliable solution is to add a Function node after the node that produces the potentially malformed JSON:
// Function to fix malformed JSON
function fixMalformedJson(jsonString) {
if (!jsonString || typeof jsonString !== 'string') {
return null;
}
try {
// First, try to parse as is
return JSON.parse(jsonString);
} catch (e) {
// If parsing fails, attempt repairs
console.log('JSON parse error. Attempting to fix malformed JSON:', e.message);
let fixedJson = jsonString;
// Fix 1: Balance braces and brackets
const openBraces = (fixedJson.match(/{/g) || []).length;
const closeBraces = (fixedJson.match(/}/g) || []).length;
const openBrackets = (fixedJson.match(/[/g) || []).length;
const closeBrackets = (fixedJson.match(/]/g) || []).length;
// Add missing closing braces
for (let i = 0; i < openBraces - closeBraces; i++) {
fixedJson += '}';
}
// Add missing closing brackets
for (let i = 0; i < openBrackets - closeBrackets; i++) {
fixedJson += ']';
}
// Fix 2: Remove trailing commas
fixedJson = fixedJson.replace(/,\s_}/g, '}').replace(/,\s_]/g, ']');
// Fix 3: Try to fix unquoted property names
fixedJson = fixedJson.replace(/(\w+):/g, '"$1":');
try {
return JSON.parse(fixedJson);
} catch (e2) {
console.log('Could not repair JSON:', e2.message);
// If all else fails, return a default valid JSON object with the original text
return {
error: "Malformed JSON could not be repaired",
originalText: jsonString,
attemptedFix: fixedJson
};
}
}
}
// Get the output from the previous node
const modelOutput = $input.item.json.output || $input.item.json.response || $input.item.json.content || $input.item.json;
let result;
// Handle different data types
if (typeof modelOutput === 'string') {
// Try to fix and parse the JSON string
result = fixMalformedJson(modelOutput);
} else if (typeof modelOutput === 'object') {
// If it's already an object, no need to parse
result = modelOutput;
} else {
// Convert to string and try to fix
result = fixMalformedJson(String(modelOutput));
}
// Return the fixed result
return {
json: {
fixedOutput: result
}
};
Step 3: Handle Specific JSON Structure Issues
If your AI model outputs JSON with a specific structure that requires custom handling, modify the Function node code. For example, if you're working with OpenAI's API that outputs JSON in a specific format, you might need a more targeted approach:
// For OpenAI specific outputs
function extractJsonFromOpenAI(response) {
try {
// If it's already a valid JSON object
if (typeof response === 'object') {
return response;
}
// If it's a string containing JSON
if (typeof response === 'string') {
// Look for JSON object patterns
const jsonMatch = response.match(/{[\s\S]\*}/);
if (jsonMatch) {
try {
return JSON.parse(jsonMatch[0]);
} catch (e) {
// If direct parsing fails, try the general fix
return fixMalformedJson(jsonMatch[0]);
}
}
// Look for JSON array patterns
const arrayMatch = response.match(/[[\s\S]\*]/);
if (arrayMatch) {
try {
return JSON.parse(arrayMatch[0]);
} catch (e) {
// If direct parsing fails, try the general fix
return fixMalformedJson(arrayMatch[0]);
}
}
}
// If no JSON structure is found, return a default object
return {
error: "No JSON structure found",
originalText: response
};
} catch (e) {
console.log('Error processing OpenAI response:', e.message);
return {
error: e.message,
originalText: response
};
}
}
// Get the OpenAI response
const openAIResponse = $input.item.json.response || $input.item.json;
// Process and return the result
return {
json: {
fixedOutput: extractJsonFromOpenAI(openAIResponse)
}
};
Step 4: Set Up JSON Validation
To ensure your fixed JSON conforms to a specific schema, add validation logic:
function validateJsonStructure(jsonObj, expectedSchema) {
// Basic schema validation
if (!jsonObj) {
return false;
}
for (const requiredField of expectedSchema) {
if (!(requiredField in jsonObj)) {
return false;
}
}
return true;
}
// Get the fixed JSON from previous steps
const fixedJson = $input.item.json.fixedOutput;
// Define your expected schema
const expectedSchema = ['name', 'age', 'email']; // Replace with your required fields
// Validate and handle accordingly
if (validateJsonStructure(fixedJson, expectedSchema)) {
return {
json: {
valid: true,
data: fixedJson
}
};
} else {
// Create a default structure if validation fails
return {
json: {
valid: false,
data: {
name: fixedJson.name || "Unknown",
age: fixedJson.age || 0,
email: fixedJson.email || "[email protected]"
},
originalData: fixedJson
}
};
}
Step 5: Configure Error Handling for the Workflow
Set up proper error handling in your n8n workflow:
// In the Set node connected to the Error Trigger
return {
json: {
error: true,
message: `Workflow error: ${$input.item.json.error}`,
nodeWithError: $input.item.json.node,
timestamp: new Date().toISOString()
}
};
Step 6: Implement Retry Logic
For more robust handling, implement a retry mechanism for model outputs:
// In a Function node
const maxRetries = 3;
const currentRetry = $input.item.json.retryCount || 0;
// If we haven't hit max retries and we have an error
if (currentRetry < maxRetries && $input.item.json.error) {
return {
json: {
retryCount: currentRetry + 1,
shouldRetry: true,
lastError: $input.item.json.error
}
};
} else if (currentRetry >= maxRetries) {
// We've hit max retries, give up and return an error
return {
json: {
retryCount: currentRetry,
shouldRetry: false,
error: "Max retries exceeded. Could not fix JSON.",
lastError: $input.item.json.error
}
};
} else {
// No error or retry needed
return $input.item;
}
Step 7: Test Your Solution
After implementing these fixes:
Step 8: Create a Reusable Subworkflow
For reusability across multiple workflows:
This creates a reusable component you can add to any workflow where JSON parse errors might occur.
Step 9: Monitor and Improve
Set up ongoing monitoring to catch and improve JSON error handling:
// In a Function node for logging
function logJsonError(error, input) {
// You could send this to a logging service
console.log('JSON Error Log:', {
timestamp: new Date().toISOString(),
error: error.message || error,
inputSample: typeof input === 'string' ? input.substring(0, 100) + '...' : 'Non-string input',
workflowId: $workflow.id,
runId: $execution.id
});
}
try {
// Your JSON parsing code here
} catch (e) {
logJsonError(e, $input.item.json);
throw e; // Re-throw to trigger error handling
}
Step 10: Advanced Techniques for Complex Cases
For highly complex JSON structures or particularly problematic AI outputs, consider these advanced techniques:
function advancedJsonRepair(input) {
// Advanced state machine for JSON parsing
const chars = input.split('');
let result = '';
let inString = false;
let escapeNext = false;
let bracketStack = [];
let braceStack = [];
for (let i = 0; i < chars.length; i++) {
const char = chars[i];
const nextChar = chars[i + 1] || '';
// String handling logic
if (char === '"' && !escapeNext) {
inString = !inString;
}
// Escape character handling
if (char === '\\' && !escapeNext) {
escapeNext = true;
} else {
escapeNext = false;
}
// Track brackets and braces
if (!inString) {
if (char === '{') {
braceStack.push(i);
} else if (char === '}') {
braceStack.pop();
} else if (char === '[') {
bracketStack.push(i);
} else if (char === ']') {
bracketStack.pop();
}
// Fix common issues
if (char === ',' && (nextChar === '}' || nextChar === ']')) {
continue; // Skip trailing commas
}
}
result += char;
}
// Balance any remaining open brackets/braces
while (braceStack.length > 0) {
braceStack.pop();
result += '}';
}
while (bracketStack.length > 0) {
bracketStack.pop();
result += ']';
}
return result;
}
// Get input and try to repair
const input = $input.item.json.output || '';
const repairedJson = advancedJsonRepair(input);
try {
const parsedJson = JSON.parse(repairedJson);
return { json: { fixedOutput: parsedJson, repaired: true } };
} catch (e) {
return {
json: {
error: "Could not repair JSON even with advanced methods",
attemptedFix: repairedJson,
originalText: input
}
};
}
Conclusion
JSON parse errors are common when working with AI model outputs in n8n workflows. By implementing a robust error handling and repair strategy using Function nodes, you can create resilient workflows that handle malformed JSON gracefully. The key is to identify the specific types of JSON malformations you're encountering and implement targeted fixes.
For ongoing maintenance, continue to monitor and refine your JSON error handling logic as you encounter new edge cases. With proper implementation, your n8n workflows can become self-healing and more reliable when working with unpredictable AI model outputs.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.