Keywords: fetch API | CORS | opaque response
Abstract: This article provides an in-depth analysis of the "Unexpected end of input" error encountered when using the JavaScript fetch() API. It explores common causes, with a focus on opaque response types due to CORS restrictions, detailing their characteristics and limitations on data reading. Multiple solutions are presented, including server-side CORS enablement and client-side handling of empty response bodies. Through code examples and step-by-step explanations, the article helps developers understand the error mechanisms and master effective debugging and fixing techniques.
Problem Background and Error Phenomenon
When using the JavaScript fetch() API to retrieve data from a server, developers may encounter the error: Uncaught (in promise) SyntaxError: Unexpected end of input at fetch.then.blob. This error typically occurs when attempting to parse the response body as JSON, but the body is empty or unreadable. For example, in the following code:
const weatherAPi = 'https://www.metaweather.com/api/location/523920';
fetch(weatherAPi, {
mode: 'no-cors'
}).then(blob => blob.json())
.then(data => console.log(data))If the server response is empty or unreadable due to cross-origin restrictions, calling the blob.json() method throws the "Unexpected end of input" error. This is primarily because fetch() cannot properly handle empty or invalid JSON strings during parsing.
Core Cause: Opaque Response Type and CORS Restrictions
A common cause is the use of the mode: 'no-cors' option. When a fetch() request targets a cross-origin resource and the server does not set CORS headers, the response type is "opaque". Opaque responses are severely restricted, with characteristics including: type as "opaque", URL list empty, status code 0, status message empty, header list empty, body null. This means the response data cannot be read or the request status checked.
In code, if the response object is logged first, its type can be seen as "opaque". For example:
fetch(weatherAPi, { mode: 'no-cors' })
.then(response => {
console.log(response.type); // Output: opaque
return response.json(); // This will throw an error
})Since the body of an opaque response is null, calling response.json() attempts to parse empty data, leading to the "Unexpected end of input" error. This behavior aligns with the Fetch specification, designed to prevent cross-origin security vulnerabilities.
Solution One: Enable CORS Support on the Server
To resolve this issue, the preferred method is to enable CORS support on the server side. This can be achieved by configuring the server to add appropriate HTTP headers, such as Access-Control-Allow-Origin. Implementation depends on the server environment:
- In Nginx, modify the server configuration file to add CORS headers.
- In PHP, use code like:
<?php header("Access-Control-Allow-Origin: *"); // Allow all origins, or specify e.g., http://www.example.com ?>
After enabling CORS, fetch() requests no longer use no-cors mode, and the response type becomes "cors", allowing data reading and status checking. Modified code example:
fetch(weatherAPi) // Remove mode option or use default
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));This code adds status checks to ensure the response is successful before parsing JSON, improving robustness.
Solution Two: Client-Side Handling of Empty Response Bodies
If the server response might be empty, or if CORS settings cannot be modified, use the response.text() method on the client side instead of response.json(). response.text() does not throw an error with empty responses but returns an empty string. Then, manually check and parse JSON. Example code:
fetch(weatherAPi, { mode: 'no-cors' })
.then(response => response.text())
.then(data => {
const parsedData = data ? JSON.parse(data) : {}; // If data is empty, use an empty object
console.log(parsedData);
})
.catch(error => console.error('Error:', error));This approach avoids the "Unexpected end of input" error because response.text() does not fail with empty bodies. However, note that data may still be unreadable under opaque responses, so it is best combined with server-side CORS enablement.
In-Depth Analysis and Best Practices
According to reference articles, similar issues may stem from response bodies being null despite a status code of 200, causing JSON parsing to fail. During debugging, step-by-step checks of response status and body content are recommended. Add error handling and logging to the fetch() chain, for example:
fetch(weatherAPi)
.then(response => {
console.log('Response status:', response.status);
console.log('Response type:', response.type);
return response.text(); // Get text first to avoid direct JSON parsing
})
.then(text => {
if (text) {
return JSON.parse(text);
} else {
return {}; // Handle empty response
}
})
.then(data => console.log('Data:', data))
.catch(error => console.error('Fetch error:', error));Best practices include: always checking response status, using the response.ok property, handling potential empty responses, and enabling detailed logging in development environments. For cross-origin requests, prioritize server CORS configuration and avoid no-cors mode unless necessary and its limitations are understood.
Conclusion
The "Unexpected end of input" error in the fetch() API is common with opaque responses or empty response bodies. By understanding CORS mechanisms and response types, developers can adopt solutions such as server-side CORS enablement or client-side optimizations. Code examples demonstrate step-by-step debugging and fixes, emphasizing error handling and robust design. In practice, choose appropriate methods based on specific scenarios to ensure secure and reliable cross-origin data interactions in web applications.