error http
HTTP 422 Unprocessable Entity
Understanding HTTP 422 Unprocessable Entity - the server understands the request but cannot process it due to semantic errors in the content.
What It Means
HTTP 422 Unprocessable Entity indicates that the server understands the content type of the request entity, and the syntax of the request entity is correct, but it was unable to process the contained instructions due to semantic errors.
The key distinction from 400 Bad Request: with 422, the request is well-formed (valid JSON, correct Content-Type), but the data itself fails validation rules.
Common Causes
- Form validation failures (invalid email format, password too short)
- Business logic validation errors (end date before start date)
- Required fields present but with invalid values
- Data type mismatches (string where number expected)
- Values outside acceptable ranges
- Referencing non-existent related resources
How to Fix
Server-side validation (Express.js)
app.post('/api/users', async (req, res) => {
const errors = [];
const { email, password, age } = req.body;
if (!email || !email.includes('@')) {
errors.push({ field: 'email', message: 'Valid email is required' });
}
if (!password || password.length < 8) {
errors.push({ field: 'password', message: 'Password must be at least 8 characters' });
}
if (age && (age < 0 || age > 150)) {
errors.push({ field: 'age', message: 'Age must be between 0 and 150' });
}
if (errors.length > 0) {
return res.status(422).json({
error: 'Unprocessable Entity',
message: 'Validation failed',
details: errors
});
}
const user = await User.create(req.body);
res.status(201).json(user);
});
With Zod validation
import { z } from 'zod';
const UserSchema = z.object({
email: z.string().email('Invalid email format'),
password: z.string().min(8, 'Password must be at least 8 characters'),
name: z.string().min(1, 'Name is required'),
age: z.number().int().min(0).max(150).optional()
});
app.post('/api/users', (req, res) => {
const result = UserSchema.safeParse(req.body);
if (!result.success) {
return res.status(422).json({
error: 'Validation Error',
details: result.error.issues
});
}
// Process valid data in result.data
});
Client-side handling
async function submitForm(data) {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (response.status === 422) {
const { details } = await response.json();
// Display field-level errors
details.forEach(error => {
const field = document.querySelector(`[name="${error.field}"]`);
showFieldError(field, error.message);
});
return;
}
return response.json();
}
Django REST Framework
from rest_framework import serializers, status
from rest_framework.response import Response
class UserSerializer(serializers.Serializer):
email = serializers.EmailField(required=True)
password = serializers.CharField(min_length=8)
age = serializers.IntegerField(min_value=0, max_value=150, required=False)
# DRF automatically returns 400 for validation errors
# To return 422 instead:
class UserView(APIView):
def post(self, request):
serializer = UserSerializer(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors, status=422)
return Response(serializer.validated_data, status=201)