Fix “unexpected token” JSON errors from Claude in n8n by cleaning, extracting, and properly formatting JSON responses using code nodes, regex, and improved prompts.
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 "unexpected token" errors when parsing JSON from Claude in n8n, you'll need to properly sanitize and format the JSON string returned by Claude. This typically happens because Claude sometimes adds extra text, markdown formatting, or doesn't properly escape characters in JSON responses, causing parsing errors in n8n's JSON node.
Step 1: Understand the Problem
When working with Claude AI in n8n workflows, you might encounter an "unexpected token" error when trying to parse JSON data returned by Claude. This happens because:
Step 2: Examine Your Error Message
Before attempting to fix the issue, examine the specific error message you're getting. It usually looks something like:
SyntaxError: Unexpected token x in JSON at position y
This tells you exactly where the parsing is failing. Look at position "y" in your JSON string to find the problematic character.
Step 3: Set Up Proper Prompting for Claude
The first solution is to properly instruct Claude to generate clean, valid JSON:
// Example prompt to Claude
Return a valid JSON object with the following structure. Return ONLY the JSON with no explanation or additional text:
{
"name": "string",
"age": number,
"items": ["array", "of", "strings"]
}
Be extremely specific about wanting only the JSON response with no additional text or explanation.
Step 4: Use JavaScript Code to Extract and Clean the JSON
Add a "Code" node in n8n before your JSON parse node to clean Claude's response:
// Get the raw response from Claude
const claudeResponse = items[0].json.response || items[0].json.body || items[0].json;
// Function to extract JSON from Claude's response
function extractJSON(text) {
// Try to find JSON object/array using regex
const jsonRegex = /{[\s\S]_}|[[\s\S]_]/;
const match = text.match(jsonRegex);
if (match) {
return match[0];
}
return text;
}
// Function to clean up common JSON issues
function cleanJSON(jsonString) {
// Remove any markdown code block indicators
let cleaned = jsonString.replace(/`json|`/g, '');
// Trim whitespace
cleaned = cleaned.trim();
// Replace any incorrectly escaped quotes
cleaned = cleaned.replace(/(?
Step 5: Using Regular Expressions to Extract JSON
If Claude consistently wraps JSON in a certain way, you can use a more specific regex pattern:
// For JSON wrapped in code blocks
function extractJSONFromCodeBlock(text) {
const codeBlockRegex = /`(?:json)?\s*([\s\S]*?)\s*`/;
const match = text.match(codeBlockRegex);
if (match && match[1]) {
return match[1].trim();
}
// Fallback to more general JSON extraction
return extractJSON(text);
}
// Example usage
const extractedJSON = extractJSONFromCodeBlock(claudeResponse);
Step 6: Implement a Function Node Solution
Here's a complete Function node implementation you can add to your workflow:
// Get Claude's response
const claudeResponse = items[0].json.response || items[0].json.content || items[0].json.body || items[0].json;
// Comprehensive JSON extraction and cleaning function
function extractAndCleanJSON(text) {
// First try to find content within code blocks
let codeBlockRegex = /`(?:json)?\s*([\s\S]*?)\s*`/;
let match = text.match(codeBlockRegex);
if (match && match[1]) {
text = match[1].trim();
}
// If that didn't work, try to find JSON patterns
if (!text.startsWith('{') && !text.startsWith('[')) {
const jsonRegex = /({[\s\S]_}|[[\s\S]_])/;
match = text.match(jsonRegex);
if (match && match[1]) {
text = match[1];
}
}
// Clean the extracted text
text = text.trim();
// Remove any invisible characters or BOM
text = text.replace(/^\uFEFF/, '');
// Fix common JSON formatting issues
text = text.replace(/\\'/g, "'");
text = text.replace(/\\"/g, '\\"');
text = text.replace(/"{/g, '{');
text = text.replace(/}"/g, '}');
text = text.replace(/"[/g, '[');
text = text.replace(/]"/g, ']');
return text;
}
// Process the response
try {
const cleaned = extractAndCleanJSON(claudeResponse);
const parsed = JSON.parse(cleaned);
return [{json: parsed}];
} catch (error) {
// Return detailed error for debugging
return [{
json: {
error: `JSON parsing failed: ${error.message}`,
originalText: claudeResponse,
cleanedText: extractAndCleanJSON(claudeResponse)
}
}];
}
Step 7: Use the Set Node for Simple JSON Extraction
For simpler cases, you can use n8n's Set node to extract JSON:
// In the Set node, create a new field:
Field name: cleanedJSON
Type: Expression
Expression: {{ $json.response.replace(/`json|`/g, '').trim() }}
Step 8: Modify Your Claude Prompt Template
Improve your prompting to get cleaner JSON:
I need a JSON object with the following schema.
IMPORTANT INSTRUCTIONS:
1. Respond ONLY with the JSON object
2. Do not include backticks, markdown formatting, or explanatory text
3. Ensure all JSON is properly escaped
4. Do not include any text before or after the JSON
Schema:
{
"property1": "value1",
"property2": 123,
"property3": [true, false]
}
Step 9: Test with a JSON Validation Node
Create a small sub-workflow to validate JSON before parsing:
function validateJSON(jsonString) {
try {
JSON.parse(jsonString);
return true;
} catch (e) {
return false;
}
}
const response = items[0].json.response;
// Try different extraction methods until one works
const methods = [
// Method 1: Direct parsing
response,
// Method 2: Extract code blocks
response.match(/`(?:json)?\s*([\s\S]*?)\s*`/)?.[1],
// Method 3: Find JSON pattern
response.match(/({[\s\S]_}|[[\s\S]_])/)?.[1],
// Method 4: Clean and try again
response.replace(/`json|`/g, '').trim()
];
// Test each method
for (const method of methods) {
if (method && validateJSON(method)) {
return [{
json: {
valid: true,
parsedJSON: JSON.parse(method),
method: methods.indexOf(method) + 1
}
}];
}
}
// If all methods fail
return [{
json: {
valid: false,
originalResponse: response,
message: "Could not extract valid JSON using any method"
}
}];
Step 10: Implement a Fallback Mechanism
Create a workflow with fallback paths for when JSON parsing fails:
{{$json.valid === true}}
)
Step 11: Use a Multi-Stage Approach for Complex Cases
For consistently problematic JSON responses, implement a multi-stage approach:
// Stage 1: Get Claude to generate JSON with a clear prompt
const initialPrompt = \`
Generate a valid JSON object with user information.
RESPOND ONLY WITH THE JSON. NO OTHER TEXT.
\`;
// Stage 2: Process Claude's response
function processClaudeResponse(response) {
// First try direct parsing
try {
return JSON.parse(response);
} catch (e) {
// If that fails, try extraction methods
const cleaned = extractAndCleanJSON(response);
try {
return JSON.parse(cleaned);
} catch (e2) {
// If that fails too, try asking Claude to fix it
return {
needsFixing: true,
originalResponse: response,
cleanedAttempt: cleaned,
error: e2.message
};
}
}
}
// Stage 3: If needed, ask Claude to fix its own JSON
const fixingPrompt = \`
The JSON you provided has syntax errors and cannot be parsed.
Here is your response:
${items[0].json.originalResponse}
Please fix the JSON syntax issues and return ONLY the corrected, valid JSON with no explanations or additional text.
\`;
// Implement the stages in your workflow
const result = processClaudeResponse(items[0].json.response);
if (result.needsFixing) {
// This would trigger another request to Claude with the fixing prompt
return [{json: {needsFixing: true, fixingPrompt: fixingPrompt}}];
} else {
return [{json: result}];
}
Step 12: Implement a JSON Repair Library
For persistent issues, you can implement a more robust JSON repair mechanism using a library like "jsonrepair":
// This code simulates the jsonrepair library functionality
// In a real implementation, you would import the library
function simpleJsonRepair(text) {
// Remove comments
text = text.replace(///.\*?(\r?\n|$)/g, '');
// Replace single quotes with double quotes
text = text.replace(/'/g, '"');
// Add missing quotes to property names
text = text.replace(/({|,)\s_([a-zA-Z0-9\_]+)\s_:/g, '$1"$2":');
// Fix trailing commas
text = text.replace(/,\s\*(}|])/g, '$1');
// Replace unquoted text values with quoted ones
// This is simplified and would need more robust implementation
text = text.replace(/:\s_([a-zA-Z]+)\s_(,|}|])/g, ':"$1"$2');
return text;
}
// Usage in your workflow
const response = items[0].json.response;
const extracted = extractAndCleanJSON(response);
try {
const parsed = JSON.parse(extracted);
return [{json: parsed}];
} catch (e) {
// Try to repair the JSON
const repaired = simpleJsonRepair(extracted);
try {
const parsed = JSON.parse(repaired);
return [{json: parsed}];
} catch (e2) {
return [{
json: {
error: "JSON parsing failed even after repair",
original: response,
extracted: extracted,
repaired: repaired
}
}];
}
}
Step 13: Create a Custom n8n Node for Claude JSON Handling
For advanced users, you can create a custom n8n node specifically for handling Claude's JSON responses:
Here's a simplified example of what that node's core functionality might look like:
class ClaudeJsonExtractor {
constructor() {
this.methods = [
this.directParse,
this.extractCodeBlock,
this.findJsonPattern,
this.cleanAndParse,
this.repairAndParse
];
}
process(input) {
// Try each method until one works
for (const method of this.methods) {
try {
return {
success: true,
data: method.call(this, input),
methodUsed: method.name
};
} catch (e) {
// Continue to next method
}
}
// If all methods fail
return {
success: false,
originalInput: input,
error: "All JSON extraction methods failed"
};
}
directParse(input) {
return JSON.parse(input);
}
extractCodeBlock(input) {
const match = input.match(/`(?:json)?\s*([\s\S]*?)\s*`/);
if (!match || !match[1]) throw new Error("No code block found");
return JSON.parse(match[1].trim());
}
findJsonPattern(input) {
const match = input.match(/({[\s\S]_}|[[\s\S]_])/);
if (!match || !match[1]) throw new Error("No JSON pattern found");
return JSON.parse(match[1]);
}
cleanAndParse(input) {
let cleaned = input.replace(/`json|`/g, '').trim();
cleaned = cleaned.replace(/\\'/g, "'");
cleaned = cleaned.replace(/\\"/g, '\\"');
return JSON.parse(cleaned);
}
repairAndParse(input) {
// Simplified repair logic
let repaired = input;
// Fix common issues
repaired = repaired.replace(/'/g, '"');
repaired = repaired.replace(/({|,)\s_([a-zA-Z0-9\_]+)\s_:/g, '$1"$2":');
repaired = repaired.replace(/,\s\*(}|])/g, '$1');
return JSON.parse(repaired);
}
}
// Usage in n8n
const extractor = new ClaudeJsonExtractor();
const result = extractor.process(items[0].json.response);
return [{json: result}];
Conclusion: Summary of Solutions
To fix "unexpected token" errors when parsing JSON from Claude in n8n:
By implementing these solutions, you can reliably parse JSON from Claude's responses in n8n workflows, even when the AI doesn't return perfectly formatted JSON.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.