Description
When a request with malformed JSON is sent to a better-call endpoint, the server returns 500 Internal Server Error instead of 400 Bad Request. This occurs because getBody() in packages/better-call/src/utils.ts does not catch SyntaxError thrown by request.json().
Current Behavior
- Malformed JSON (e.g., invalid escape sequences like
\x, \z) triggers SyntaxError in request.json()
- Error bubbles up to router's catch-all handler in
router.ts
- Returns
500 Internal Server Error with stack trace logged to console
- Makes it impossible to distinguish client input errors from actual server errors in monitoring/logs
Expected Behavior
- Malformed JSON should return
400 Bad Request
- Error message should clearly indicate invalid JSON
- No stack trace should be logged for client-side input errors
Reproduction
curl -X POST https://example.com/api/auth/sign-up/email \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com\x", "password": "test1234"}'
Current response: 500 Internal Server Error
Expected response: 400 Bad Request
Root Cause
In packages/better-call/src/utils.ts, the getBody() function:
if (jsonContentTypeRegex.test(normalizedContentType)) {
return await request.json(); // ← NO TRY/CATCH
}
The outer catch in router.ts treats this as a server error because it's not an APIError:
// For non-APIError (including JSON.parse SyntaxError):
console.error(`# SERVER_ERROR: `, error);
return new Response(null, { status: 500, statusText: "Internal Server Error" });
Proposed Solution
Wrap request.json() in try/catch and throw APIError for malformed input:
if (jsonContentTypeRegex.test(normalizedContentType)) {
try {
return await request.json();
} catch (error) {
throw new APIError(400, {
message: error instanceof SyntaxError
? `Invalid JSON: ${error.message}`
: "Malformed request body",
code: "INVALID_JSON",
});
}
}
This follows the existing pattern — getBody() already throws APIError(415) for unsupported content types.
Environment
- better-call version: via
better-auth ^1.4.18
- Runtime: Node.js (Next.js API routes)
- Discovery: Automated security assessment (malformed JSON payloads)
Description
When a request with malformed JSON is sent to a better-call endpoint, the server returns
500 Internal Server Errorinstead of400 Bad Request. This occurs becausegetBody()inpackages/better-call/src/utils.tsdoes not catchSyntaxErrorthrown byrequest.json().Current Behavior
\x,\z) triggersSyntaxErrorinrequest.json()router.ts500 Internal Server Errorwith stack trace logged to consoleExpected Behavior
400 Bad RequestReproduction
Current response:
500 Internal Server ErrorExpected response:
400 Bad RequestRoot Cause
In
packages/better-call/src/utils.ts, thegetBody()function:The outer catch in
router.tstreats this as a server error because it's not anAPIError:Proposed Solution
Wrap
request.json()in try/catch and throwAPIErrorfor malformed input:This follows the existing pattern —
getBody()already throwsAPIError(415)for unsupported content types.Environment
better-auth^1.4.18