/n8n-tutorials

How to fix “unexpected token” error when parsing JSON from Claude in n8n?

Fix “unexpected token” JSON errors from Claude in n8n by cleaning, extracting, and properly formatting JSON responses using code nodes, regex, and improved prompts.

Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Book a free consultation

How to fix “unexpected token” error when parsing JSON from Claude in n8n?

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:

  • Claude may add explanatory text before or after the actual JSON
  • Claude might format the JSON with unnecessary indentation or line breaks
  • The JSON may contain unescaped quotes or special characters
  • Claude might wrap the JSON in code blocks or add markdown formatting

 

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:

  1. Add a "Set" node after Claude's response
  2. Use JSONata or expressions to extract the JSON portion
  3. For example, if Claude always wraps JSON in code blocks, use:

// 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:

  1. Add an "IF" node after your JSON extraction attempt
  2. In the IF node, check if the parsing was successful (e.g., {{$json.valid === true}})
  3. If successful, continue with your workflow
  4. If failed, create an alternative path:
  • Option 1: Retry with a different prompt to Claude
  • Option 2: Process the text as plain text instead of JSON
  • Option 3: Send an alert or notification about the failed parsing

 

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:

  1. Set up a new n8n custom node project
  2. Implement the JSON extraction and cleaning logic
  3. Add support for various Claude response formats
  4. Package it as a reusable node

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:

  • Improve your prompts to Claude, requesting clean JSON only
  • Use a Code or Function node to extract and clean the JSON before parsing
  • Implement regex-based extraction for JSON wrapped in code blocks
  • Create a multi-stage approach for consistently problematic responses
  • Consider implementing a JSON repair mechanism for more robust handling
  • Set up fallback paths in your workflow for when parsing fails
  • For advanced cases, create a custom node specifically for Claude JSON handling

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.

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Book a Free Consultation

Client trust and success are our top priorities

When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.

Rapid Dev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with. They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

CPO, Praction - Arkady Sokolov

May 2, 2023

Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost. He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Co-Founder, Arc - Donald Muir

Dec 27, 2022

Rapid Dev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space. They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Co-CEO, Grantify - Mat Westergreen-Thorne

Oct 15, 2022

Rapid Dev is an excellent developer for no-code and low-code solutions.
We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Co-Founder, Church Real Estate Marketplace - Emmanuel Brown

May 1, 2024 

Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 
This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Production Manager, Media Production Company - Samantha Fekete

Sep 23, 2022