The "Unexpected token ) in JSON" error occurs when your React application attempts to parse invalid JSON data. This typically happens when fetching data from an API and trying to parse the response, but the response is not valid JSON—often containing extra characters, syntax errors, or non-JSON content like HTML error pages.
The "Unexpected token ) in JSON" error is thrown by JavaScript's JSON.parse() method when it encounters malformed JSON. A closing parenthesis ) is not a valid JSON character outside of a string literal. This error appears when your React component tries to parse the response from a fetch request or API call using .json() or JSON.parse(), but the content is not valid JSON. In React applications, this commonly happens when: - An API endpoint returns an error page (HTML) instead of JSON - The server response includes unexpected characters or formatting - The JSON is missing commas, has trailing commas, or has unescaped characters - The API returns a 5xx server error with an HTML error page instead of JSON - Network proxies or middleware intercept and modify the response When JSON.parse() encounters the closing parenthesis character, it doesn't know what to do with it because parentheses are not valid JSON syntax. The JSON specification only allows objects ({}), arrays ([]), strings (""), numbers, booleans (true/false), and null.
Before calling .json() on the response, log the raw response to see what you're actually receiving:
function MyComponent() {
useEffect(() => {
fetch('/api/data')
.then(response => {
// Log the entire response
console.log('Status:', response.status);
console.log('Content-Type:', response.headers.get('content-type'));
// Log the response text before parsing as JSON
return response.text();
})
.then(text => {
console.log('Response text:', text);
try {
const data = JSON.parse(text);
console.log('Parsed data:', data);
} catch (error) {
console.error('JSON parse error:', error);
console.error('Failed to parse:', text);
}
})
.catch(error => console.error('Fetch error:', error));
}, []);
return <div>Loading...</div>;
}This will show you exactly what the API is returning. If you see HTML tags, an error message, or extra characters, that's the problem.
Many APIs return error messages as HTML when something goes wrong. Check the response status and only parse as JSON on success:
fetch('/api/data')
.then(response => {
if (!response.ok) {
throw new Error(`API error: ${response.status} ${response.statusText}`);
}
return response.json();
})
.then(data => {
console.log('Success:', data);
})
.catch(error => {
console.error('Error:', error);
});The response.ok property is true for status codes 200-299. If the status is 4xx or 5xx, the server might return HTML instead of JSON.
Double-check that you're calling the correct endpoint. Common mistakes include:
// ❌ Wrong - points to HTML page instead of API
fetch('/api-docs') // This might return HTML documentation
.then(r => r.json())
.catch(e => console.error(e));
// ✅ Correct - points to actual data endpoint
fetch('/api/data')
.then(r => r.json())
.then(data => console.log(data));
// ✅ Check that API route exists in your backend
// For Next.js, verify you have: app/api/data/route.ts or pages/api/data.js
// For Express: ensure app.get('/api/data', ...) existsTest the endpoint directly in your browser's address bar to see what it returns. If you see JSON in the browser, the endpoint is correct. If you see an error page, the endpoint is wrong.
Wrap JSON parsing in a try-catch block to handle malformed JSON gracefully:
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
if (error instanceof SyntaxError) {
console.error('Invalid JSON response:', error);
// The response was not valid JSON
} else {
console.error('Fetch error:', error);
}
return null;
}
}
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
fetchData().then(setData);
}, []);
return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
}This approach safely handles JSON parsing errors without crashing your component.
Check that the API is setting the correct Content-Type header:
fetch('/api/data')
.then(response => {
const contentType = response.headers.get('content-type');
console.log('Content-Type:', contentType);
if (!contentType || !contentType.includes('application/json')) {
console.warn('Response is not JSON:', contentType);
return response.text(); // Return as plain text instead
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error(error));If the server isn't setting Content-Type: application/json, the response might contain HTML or other non-JSON content.
If using a proxy or API gateway, it might be intercepting and modifying responses. Verify your proxy configuration:
// In your package.json (for Create React App)
{
"proxy": "http://localhost:5000"
}
// Or configure in setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:5000',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
})
);
};Make sure your proxy correctly forwards JSON responses without modification.
Test your API endpoint outside of your React app to ensure it returns valid JSON:
# Using curl
curl -v http://localhost:3000/api/data
# Check the response - it should start with { or [
# Not with <html>, error messages, or other textYou can also use Postman or Insomnia to test API responses. This isolates whether the problem is in your React code or in the API itself.
If curl returns HTML or an error, the API is misconfigured. If curl returns valid JSON but React still fails, check your fetch call and error handling.
If the API returns an empty response body, JSON.parse() will fail. Handle this edge case:
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const text = await response.text();
// Handle empty response
if (!text) {
console.warn('Empty response from server');
return null;
}
return JSON.parse(text);
} catch (error) {
console.error('Error:', error);
return null;
}
}This prevents trying to parse empty strings as JSON, which would throw an error.
Server-Side Fixes: If you control the API, always return valid JSON with proper headers:
// Express.js example
app.get('/api/data', (req, res) => {
// Always set Content-Type header
res.setHeader('Content-Type', 'application/json');
try {
const data = { message: 'success' };
// Use res.json() instead of manual JSON.stringify
res.json(data);
} catch (error) {
// Return JSON error, not HTML
res.status(500).json({ error: 'Internal server error' });
}
});Debugging in DevTools: Open your browser's Network tab and inspect the failing API call. Click on the request and check the "Response" tab. If you see HTML tags or non-JSON content, that's your problem. The "Headers" tab will show the Content-Type header.
Using JavaScript JSON validator: You can use a JSON validator to check if your response is valid:
function isValidJSON(text) {
try {
JSON.parse(text);
return true;
} catch {
return false;
}
}
const response = await fetch('/api/data');
const text = await response.text();
console.log('Is valid JSON?', isValidJSON(text));API Mocking for Testing: Use libraries like msw (Mock Service Worker) to mock API responses during testing, ensuring you control the JSON format:
import { setupServer } from 'msw/node';
import { rest } from 'msw';
const server = setupServer(
rest.get('/api/data', (req, res, ctx) => {
return res(ctx.json({ message: 'success' }));
})
);
beforeAll(() => server.listen());Content negotiation: If an API can return multiple formats (JSON, XML, CSV), explicitly request JSON:
fetch('/api/data', {
headers: {
'Accept': 'application/json'
}
})
.then(r => r.json())
.catch(e => console.error(e));React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite