Fix Unexpected Token in JSON (2026)
Claude Code Error: Unexpected Token in JSON Response Fix
The “unexpected token in JSON” error is one of the most common issues developers encounter when working with Claude Code. This error typically occurs when Claude attempts to parse a response that isn’t valid JSON, breaking the communication pipeline between the AI and your tools. Understanding the root causes and having a toolkit of solutions ready will save you hours of frustration.
This guide covers diagnosis techniques, practical fixes, prevention strategies, and edge cases that catch developers off-guard. including some that aren’t well-documented anywhere else.
What Causes This Error
When Claude Code makes tool calls or interacts with external services, it expects JSON-formatted responses. The “unexpected token” error surfaces when the response contains malformed JSON. either from a tool output, an API call, or a skill that returns improperly formatted data.
The error message usually looks like one of these:
SyntaxError: Unexpected token < in JSON at position 0
SyntaxError: Unexpected token T in JSON at position 0
SyntaxError: Unexpected end of JSON input
JSON.parse: unexpected character at line 1 column 1
The specific token in the error message is actually a useful diagnostic clue. < almost always means you got an HTML response (often a 404 or error page). T usually means plain text starting with “The” or “TypeError”. Unexpected end of JSON input points to truncation. the response was cut off mid-stream.
Common scenarios triggering this error include:
- API endpoints returning HTML or plain text instead of JSON
- Tool outputs containing special characters that break JSON parsing
- Custom skills returning markdown-formatted content instead of structured data
- Network responses with encoding issues or truncated data
- Authentication failures returning HTML login pages instead of API data
- Rate-limited responses returning plain-text error messages instead of JSON error objects
- Proxy or CDN error pages intercepting requests before they reach the API
Understanding the Error in Context
Claude Code’s tool-use system works by making structured calls and expecting structured responses. The pipeline looks roughly like this:
Claude Code -> Tool call (JSON) -> Tool / API -> Response -> Claude Code parses response
When that response isn’t valid JSON, the parser throws immediately. The session doesn’t recover gracefully. it halts with the unexpected token error and your work stops.
This is by design. Claude Code doesn’t try to “guess” what a malformed response meant, because guessing incorrectly could lead to your code being written with bad assumptions about what the tool returned. The strict parsing behavior is actually a safety feature, even when it’s annoying.
Diagnosis Techniques
Before applying fixes, identify where the malformed JSON originates. Start by examining the error message itself. Claude Code usually indicates which tool or skill produced the invalid response.
Use the Read tool to inspect recent tool outputs in your conversation history. Look for responses that contain:
- Unescaped quotes or special characters
- Markdown formatting code blocks
- Incomplete JSON objects or arrays
- Non-UTF8 encoded characters
If you’re using custom skills, check the skill definition for any double-curly-brace or percent-curly-brace Liquid template syntax that might interfere with JSON parsing.
A systematic debugging checklist:
Step 1: Test the endpoint directly in your terminal
curl -s -o /tmp/api_response.json -w "%{http_code}" https://api.example.com/endpoint
Check the HTTP status code. 200 doesn't guarantee JSON
Step 2: Inspect the raw response
cat /tmp/api_response.json | head -20
If it starts with <!DOCTYPE or <html, you got HTML back
Step 3: Validate the JSON explicitly
cat /tmp/api_response.json | python3 -m json.tool
This will show exactly where parsing fails with a line/column number
Step 4: Check the Content-Type header
curl -sI https://api.example.com/endpoint | grep -i content-type
Should be application/json, not text/html or text/plain
Practical Solutions
Solution 1: Validate API Responses
When calling external APIs, ensure they return valid JSON:
import requests
import json
def call_api(url):
response = requests.get(url)
# Force JSON parsing to catch errors early
try:
data = response.json()
return data
except json.JSONDecodeError as e:
print(f"Invalid JSON response: {e}")
print(f"Response text: {response.text[:200]}")
raise
This approach catches malformed responses before they reach Claude’s parsing layer.
A more solid version that also handles HTTP error status codes:
import requests
import json
def call_api_safe(url, headers=None):
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status() # Raises for 4xx and 5xx status codes
except requests.exceptions.HTTPError as e:
# Log the full response body. often contains clues
raise RuntimeError(f"HTTP {response.status_code}: {response.text[:300]}") from e
except requests.exceptions.Timeout:
raise RuntimeError(f"Request to {url} timed out after 10 seconds")
content_type = response.headers.get("Content-Type", "")
if "application/json" not in content_type:
raise RuntimeError(
f"Expected JSON but got Content-Type: {content_type}\n"
f"Response body: {response.text[:200]}"
)
return response.json()
The raise_for_status() call is especially important. Without it, a 401 Unauthorized response that returns an HTML login page will silently flow through to the JSON parser and produce the unexpected token error. and you’ll waste time debugging JSON when the real issue is authentication.
Solution 2: Escape Special Characters in Tool Outputs
If your tool produces text with quotes or special characters, escape them properly:
Instead of echoing raw JSON
echo '{"message": "Here's a quote"}' # Breaks!
Use proper escaping
echo '{"message": "Here'"'"'s a quote"}'
Or use printf
printf '{"message": "Here'\''s a quote"}\n'
For more complex scenarios, consider using a JSON library to generate properly escaped output.
In shell scripts, the safest pattern is to avoid building JSON by hand entirely. Use a tool that knows how to serialize JSON correctly:
Fragile. manual JSON construction breaks on special characters
echo "{\"name\": \"$USERNAME\", \"path\": \"$FILE_PATH\"}"
Robust. let python handle serialization
python3 -c "
import json, sys, os
print(json.dumps({
'name': os.environ.get('USERNAME', ''),
'path': os.environ.get('FILE_PATH', '')
}))
"
Or use jq for JSON construction from shell
jq -n --arg name "$USERNAME" --arg path "$FILE_PATH" \
'{"name": $name, "path": $path}'
The jq approach is particularly clean for shell scripts because it handles all escaping automatically, including newlines, tabs, and Unicode characters that would silently corrupt manually constructed JSON.
Solution 3: Configure Your Skills Correctly
When creating custom skills using tools like the skill-creator, ensure your skill definitions don’t interfere with JSON parsing. Avoid placing unescaped curly braces in skill descriptions:
---
name: my-skill
description: Processes data using the (variable) pattern
This causes issues! Use escaped brackets or rephrase
---
Instead, structure your skill descriptions without template variables that could confuse the JSON parser.
If your skill’s markdown file needs to reference code that contains curly braces or percent-brace syntax, use literal description language rather than embedding the actual syntax. For example: “processes Jinja2-style template variables” rather than embedding a literal {{ variable }} example inside the skill definition itself.
Solution 4: Handle Truncated Responses
Truncation is a less-discussed cause of this error but a real one. Long API responses, streaming endpoints, or responses from slow network connections can arrive incomplete. The JSON parser sees a valid opening but no closing brace.
def fetch_with_length_validation(url):
response = requests.get(url, timeout=30)
content = response.text
# Check that the response ends where JSON should end
content_stripped = content.strip()
if not (
(content_stripped.startswith("{") and content_stripped.endswith("}")) or
(content_stripped.startswith("[") and content_stripped.endswith("]"))
):
raise ValueError(
f"Response appears truncated. First 50 chars: {content_stripped[:50]!r} "
f"Last 50 chars: {content_stripped[-50:]!r}"
)
return json.loads(content)
This won’t catch all truncation cases (a truncated nested object will still start and end with braces), but it catches the most common form where the response cuts off mid-field.
Working with Claude Skills
Claude skills like pdf, tdd, and frontend-design often interact with external tools and APIs. When these skills produce errors, the “unexpected token” issue frequently stems from how they handle their outputs.
For instance, when using the pdf skill to generate documents from markdown, ensure your markdown doesn’t contain inline JSON-like patterns that get misinterpreted. The docx skill handles similar scenarios by providing clearer output formatting.
The supermemory skill may encounter this error when storing or retrieving metadata. Always validate the data structure before passing it to skills that expect specific JSON schemas.
When debugging skill-related JSON errors, check whether the skill’s output is being piped into another tool. Skills that produce rich text or markdown may work fine as standalone output but fail when another skill tries to parse that output as structured data. If you’re chaining skills, make sure intermediate outputs are either valid JSON or explicitly converted to JSON before the next skill receives them.
Prevention Strategies
Implement Response Validation
Create a wrapper function that validates all responses before they reach Claude:
function validateJsonResponse(response) {
try {
const parsed = JSON.parse(response);
return { valid: true, data: parsed };
} catch (error) {
return {
valid: false,
error: error.message,
raw: response.substring(0, 100)
};
}
}
A more complete version that also normalizes common non-JSON responses:
function safeParseJson(responseText, context = "") {
if (typeof responseText !== "string") {
throw new TypeError(`Expected string response${context ? ` for ${context}` : ""}, got ${typeof responseText}`);
}
const trimmed = responseText.trim();
if (trimmed === "") {
throw new SyntaxError(`Empty response body${context ? ` from ${context}` : ""}`);
}
// Detect common non-JSON responses and give actionable errors
if (trimmed.startsWith("<!DOCTYPE") || trimmed.startsWith("<html")) {
throw new SyntaxError(
`Got HTML instead of JSON${context ? ` from ${context}` : ""}. ` +
"Check authentication headers and endpoint URL."
);
}
if (trimmed.startsWith("Error:") || trimmed.startsWith("TypeError")) {
throw new SyntaxError(
`Got plain-text error instead of JSON: ${trimmed.substring(0, 100)}`
);
}
try {
return JSON.parse(trimmed);
} catch (err) {
throw new SyntaxError(
`JSON parse failed${context ? ` for ${context}` : ""}: ${err.message}. ` +
`Response starts with: ${trimmed.substring(0, 80)!r}`
);
}
}
The context parameter is valuable in production. when you have 20 different API calls, knowing which one produced the bad response saves significant debugging time.
Use Type-Safe APIs
When building integrations, prefer APIs that guarantee JSON responses. Check documentation for content-type headers and implement fallback handling for non-JSON responses.
Test Skills in Isolation
Before deploying custom skills to production, test them with varied inputs using the xlsx skill to validate JSON outputs across different data scenarios. This is particularly important for skills that handle user-generated content.
A simple test pattern for skills:
Test a custom skill with edge-case inputs before relying on it in workflows
claude --print "Using the my-custom-skill skill: process this input: O'Brien & Associates <[email protected]>"
Check whether special characters in the input cause the skill output to break JSON parsing
Error Recovery
When you encounter this error during a session:
- Pause the current operation. Don’t continue with corrupted data
- Identify the source. Check which tool or skill produced the invalid response
- Clear and retry. Use Claude Code’s reset capability to clear the problematic context
- Fix the root cause. Apply the appropriate solution from above
- Resume with validation. Implement checks to prevent recurrence
For persistent errors that keep recurring despite fixes, consider whether the tool or API you’re calling is genuinely returning valid JSON under load. Some APIs return JSON in development but serve plain-text errors under rate limiting or high traffic. Building a retry wrapper with exponential backoff that also validates the response type on each attempt can catch this class of intermittent failures.
Conclusion
The “unexpected token in JSON response” error is manageable once you understand its origins and have a systematic approach to debugging. By validating API responses, properly escaping special characters, and carefully configuring your skills, you can prevent this error from disrupting your workflow.
Remember to test your integrations thoroughly, especially when combining multiple skills like supermemory with custom API calls. Most importantly, always implement error handling that catches JSON parsing failures early. before they reach Claude Code’s processing layer. The error message’s unexpected token character is your first diagnostic clue: use it to narrow down whether you’re dealing with an HTML response, a plain-text error, truncated data, or a genuine JSON syntax problem, then apply the appropriate fix from this guide.
Related Reading
- Claude Skill Invalid YAML Syntax Error: How to Debug. See also
- Claude Skill YAML Front Matter Parsing Error Fix. See also
- Skill MD File Format Explained With Examples. See also
- Claude Skills Troubleshooting Hub. See also
Built by theluckystrike. More at zovo.one
Find the right skill → Browse 155+ skills in our Skill Finder.
See Also
Try it: Paste your error into our Error Diagnostic for an instant fix.