12 KiB
12 KiB
Network & API Error Patterns
Common network and API errors with diagnosis and solutions.
HTTP Status Errors
400 Bad Request
HTTP 400 Bad Request
{"error": "Bad Request", "message": "Invalid JSON"}
Causes:
- Malformed JSON body
- Missing required fields
- Invalid field values
- Wrong Content-Type header
Diagnosis:
# Validate JSON
echo '{"key": "value"}' | jq .
# Check request
curl -v -X POST https://api.example.com/endpoint \
-H "Content-Type: application/json" \
-d '{"key": "value"}'
Solutions:
// Check Content-Type
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json', // Not 'text/plain'
},
body: JSON.stringify(data), // Not just data
})
// Validate before sending
if (!data.required_field) {
throw new Error('Missing required_field')
}
401 Unauthorized
HTTP 401 Unauthorized
{"error": "Unauthorized", "message": "Invalid or expired token"}
Causes:
- Missing auth token
- Expired token
- Invalid token format
- Wrong auth scheme
Solutions:
// Check Authorization header format
fetch(url, {
headers: {
'Authorization': `Bearer ${token}`, // Note: "Bearer " prefix
},
})
// Refresh token if expired
async function fetchWithAuth(url) {
let response = await fetch(url, {
headers: { 'Authorization': `Bearer ${accessToken}` }
})
if (response.status === 401) {
accessToken = await refreshToken()
response = await fetch(url, {
headers: { 'Authorization': `Bearer ${accessToken}` }
})
}
return response
}
403 Forbidden
HTTP 403 Forbidden
{"error": "Forbidden", "message": "Insufficient permissions"}
Causes:
- Authenticated but not authorized
- Resource access denied
- Rate limiting
- IP blocked
Different from 401: 401 = "Who are you?" 403 = "I know who you are, but no."
Solutions:
- Check user permissions/roles
- Verify API key scopes
- Check rate limit headers
- Verify IP whitelist
404 Not Found
HTTP 404 Not Found
{"error": "Not Found", "message": "Resource not found"}
Causes:
- Wrong URL/endpoint
- Resource deleted
- ID doesn't exist
- Trailing slash issues
Diagnosis:
# Check URL is correct
curl -I https://api.example.com/users/123
# Try with/without trailing slash
curl https://api.example.com/users/
curl https://api.example.com/users
Solutions:
// Handle 404 gracefully
const response = await fetch(`/api/users/${id}`)
if (response.status === 404) {
return null // Or throw custom error
}
return response.json()
405 Method Not Allowed
HTTP 405 Method Not Allowed
{"error": "Method Not Allowed", "allowed": ["GET", "POST"]}
Causes:
- Using wrong HTTP method
- Endpoint doesn't support method
Solutions:
# Check allowed methods
curl -I -X OPTIONS https://api.example.com/endpoint
# Look for Allow header
# Use correct method
curl -X POST https://api.example.com/users # Not GET
429 Too Many Requests
HTTP 429 Too Many Requests
{"error": "Rate Limited", "retry_after": 60}
Causes:
- Rate limit exceeded
- Too many concurrent requests
Solutions:
// Implement exponential backoff
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url)
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 60
await sleep(retryAfter * 1000 * (i + 1))
continue
}
return response
}
throw new Error('Max retries exceeded')
}
// Rate limiting in client
const limiter = new RateLimiter({
tokensPerInterval: 10,
interval: 'second',
})
async function limitedFetch(url) {
await limiter.removeTokens(1)
return fetch(url)
}
500 Internal Server Error
HTTP 500 Internal Server Error
Causes:
- Server-side bug
- Unhandled exception
- Database error
- Third-party service failure
What you can do:
- Check if your request is valid
- Retry later
- Report to API provider with request details
502 Bad Gateway
HTTP 502 Bad Gateway
Causes:
- Upstream server down
- Proxy misconfiguration
- Load balancer issues
Solutions:
- Usually temporary - retry later
- Check service status page
- Implement retry logic
503 Service Unavailable
HTTP 503 Service Unavailable
Causes:
- Server overloaded
- Maintenance mode
- Capacity issues
Solutions:
// Helper function
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms))
// Retry with exponential backoff
async function fetchWithBackoff(url) {
const maxRetries = 5
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url)
if (response.status === 503) {
await sleep(Math.pow(2, i) * 1000) // 1s, 2s, 4s, 8s, 16s
continue
}
return response
} catch (error) {
if (i === maxRetries - 1) throw error
await sleep(Math.pow(2, i) * 1000)
}
}
throw new Error('Max retries exceeded')
}
504 Gateway Timeout
HTTP 504 Gateway Timeout
Causes:
- Upstream server too slow
- Long-running operation
- Network timeout
Solutions:
- Increase timeout settings
- Use async/webhook pattern for long operations
- Implement pagination for large responses
Connection Errors
ECONNREFUSED
Error: connect ECONNREFUSED 127.0.0.1:3000
Causes:
- Server not running
- Wrong port
- Firewall blocking
Diagnosis:
# Check if port is listening
lsof -i :3000
netstat -an | grep 3000
curl -v http://localhost:3000
Solutions:
- Start the server
- Check port configuration
- Check firewall rules
ENOTFOUND
Error: getaddrinfo ENOTFOUND api.example.com
Causes:
- Invalid hostname
- DNS resolution failure
- No internet connection
Diagnosis:
# Test DNS
nslookup api.example.com
dig api.example.com
ping api.example.com
Solutions:
- Check URL spelling
- Try different DNS (8.8.8.8)
- Check internet connection
- Check /etc/hosts file
ETIMEDOUT
Error: connect ETIMEDOUT
Causes:
- Server not responding
- Firewall silently dropping
- Network congestion
Solutions:
// Increase timeout
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 30000)
try {
const response = await fetch(url, { signal: controller.signal })
clearTimeout(timeoutId)
return response
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Request timed out')
}
throw error
}
ECONNRESET
Error: read ECONNRESET
Causes:
- Server closed connection unexpectedly
- Network interruption
- Server crashed
Solutions:
// Implement retry logic
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await fetch(url)
} catch (error) {
if (error.code === 'ECONNRESET' && i < retries - 1) {
await sleep(1000 * (i + 1))
continue
}
throw error
}
}
}
SSL/TLS Errors
UNABLE_TO_VERIFY_LEAF_SIGNATURE
Error: unable to verify the first certificate
Causes:
- Self-signed certificate
- Missing intermediate certificate
- Expired certificate
Solutions:
// Development only - disable verification (NOT FOR PRODUCTION)
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
// Better - add CA certificate
const https = require('https')
const agent = new https.Agent({
ca: fs.readFileSync('ca-cert.pem'),
})
fetch(url, { agent })
CERT_HAS_EXPIRED
Error: certificate has expired
Solutions:
- Update server certificate
- Check system time is correct
- Update CA certificates:
update-ca-certificates
CORS Errors
Access-Control-Allow-Origin
Access to fetch at 'https://api.example.com' from origin 'https://myapp.com'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
Causes:
- Server doesn't allow cross-origin requests
- Missing CORS headers
- Credentials mode mismatch
Solutions (Server-side):
// Express.js
const cors = require('cors')
app.use(cors({
origin: 'https://myapp.com', // Or '*' for all (not recommended)
credentials: true,
}))
// Manual headers
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://myapp.com')
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
res.header('Access-Control-Allow-Credentials', 'true')
next()
})
Solutions (Client-side workarounds):
// Use proxy in development
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
}
}
}
}
// Then fetch from /api instead
fetch('/api/endpoint')
Preflight Request Failed
Response to preflight request doesn't pass access control check
Causes:
- OPTIONS request not handled
- Missing preflight headers
Solutions:
// Handle OPTIONS request
app.options('*', cors()) // Express with cors
// Or manually
app.options('*', (req, res) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
res.sendStatus(200)
})
JSON Parsing Errors
Unexpected token < in JSON
SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
Causes:
- Server returned HTML instead of JSON (error page, 404)
- Wrong endpoint
- Auth redirect
Diagnosis:
const response = await fetch(url)
console.log('Status:', response.status)
console.log('Content-Type:', response.headers.get('content-type'))
const text = await response.text()
console.log('Body:', text.substring(0, 200))
Solutions:
// Check response before parsing
const response = await fetch(url)
if (!response.ok) {
const text = await response.text()
throw new Error(`HTTP ${response.status}: ${text}`)
}
const contentType = response.headers.get('content-type')
if (!contentType?.includes('application/json')) {
throw new Error(`Expected JSON, got ${contentType}`)
}
return response.json()
Unexpected end of JSON input
SyntaxError: Unexpected end of JSON input
Causes:
- Empty response body
- Truncated response
- Network interruption
Solutions:
// Check for empty response
const text = await response.text()
if (!text) {
return null // Or default value
}
return JSON.parse(text)
Quick Reference Table
| Error | Category | Quick Fix |
|---|---|---|
| 400 Bad Request | Client | Check JSON format, Content-Type |
| 401 Unauthorized | Auth | Check token, refresh if expired |
| 403 Forbidden | Auth | Check permissions, rate limits |
| 404 Not Found | Client | Verify URL, check resource exists |
| 429 Too Many Requests | Rate Limit | Implement backoff, reduce requests |
| 500 Internal Server Error | Server | Retry, report to provider |
| 503 Service Unavailable | Server | Retry with exponential backoff |
| ECONNREFUSED | Connection | Start server, check port |
| ENOTFOUND | DNS | Check hostname, DNS settings |
| ETIMEDOUT | Timeout | Increase timeout, check network |
| CORS blocked | Browser | Add CORS headers server-side |
| Unexpected token < | Parse | Check response is JSON not HTML |