Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,15 @@ export class InvalidFileType extends Error {

export class ValidationError extends Error {
code = 'VALIDATION'
status = 422
/**
* HTTP status code for this validation error.
*
* - Response validation errors (type === 'response') return 500 (Internal Server Error)
* because they indicate a server-side bug where the response doesn't match the schema.
* - Request validation errors (query, body, headers, params, cookie) return 422 (Unprocessable Entity)
* because they indicate client sent invalid data.
*/
status: number

/**
* An actual value of `message`
Expand Down Expand Up @@ -481,6 +489,10 @@ export class ValidationError extends Error {
this.expected = expected
this.customError = customError

// Response validation errors are server bugs (500), not client errors (422)
// If the server returns data that doesn't match its own schema, that's an internal error
this.status = type === 'response' ? 500 : 422

Object.setPrototypeOf(this, ValidationError.prototype)
}

Expand Down Expand Up @@ -546,7 +558,7 @@ export class ValidationError extends Error {

toResponse(headers?: Record<string, any>) {
return new Response(this.message, {
status: 400,
status: this.status,
headers: {
...headers,
'content-type': 'application/json'
Expand Down
15 changes: 10 additions & 5 deletions test/core/as.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ describe('as', () => {
])

expect(called).toBe(3)
expect(response).toEqual([422, 422, 422])
// Response validation errors return 500 (server error) - see issue #1480
expect(response).toEqual([500, 500, 500])
})

it('handle as global with local override', async () => {
Expand Down Expand Up @@ -141,7 +142,8 @@ describe('as', () => {
])

expect(called).toBe(4)
expect(response).toEqual([422, 200, 422])
// Response validation errors return 500 (server error) - see issue #1480
expect(response).toEqual([500, 200, 500])
})

it('handle as global with scoped override', async () => {
Expand Down Expand Up @@ -178,7 +180,8 @@ describe('as', () => {
])

expect(called).toBe(5)
expect(response).toEqual([422, 200, 200])
// Response validation errors return 500 (server error) - see issue #1480
expect(response).toEqual([500, 200, 200])
})

it('handle as scoped', async () => {
Expand Down Expand Up @@ -209,7 +212,8 @@ describe('as', () => {
])

expect(called).toBe(2)
expect(response).toEqual([422, 422, 200])
// Response validation errors return 500 (server error) - see issue #1480
expect(response).toEqual([500, 500, 200])
})

it('handle as scoped twice', async () => {
Expand Down Expand Up @@ -242,6 +246,7 @@ describe('as', () => {
])

expect(called).toBe(3)
expect(response).toEqual([422, 422, 422])
// Response validation errors return 500 (server error) - see issue #1480
expect(response).toEqual([500, 500, 500])
})
})
16 changes: 10 additions & 6 deletions test/core/dynamic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,8 +529,9 @@ describe('Dynamic Mode', () => {
.handle(req('/valid-201'))
.then((x) => x.status)

expect(invalid).toBe(422)
expect(invalid201).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(invalid).toBe(500)
expect(invalid201).toBe(500)
expect(valid).toBe(200)
expect(valid201).toBe(201)
})
Expand All @@ -552,7 +553,8 @@ describe('Dynamic Mode', () => {
const invalid = await app.handle(req('/invalid')).then((x) => x.status)
const valid = await app.handle(req('/valid')).then((x) => x.json())

expect(invalid).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(invalid).toBe(500)
expect(valid).toEqual({
foo: 'bar'
})
Expand Down Expand Up @@ -608,8 +610,9 @@ describe('Dynamic Mode', () => {
.handle(req('/valid-201'))
.then((x) => x.status)

expect(invalid).toBe(422)
expect(invalid201).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(invalid).toBe(500)
expect(invalid201).toBe(500)
expect(valid).toBe(200)
expect(valid201).toBe(201)
})
Expand All @@ -634,7 +637,8 @@ describe('Dynamic Mode', () => {
const invalid = await app.handle(req('/invalid')).then((x) => x.status)
const valid = await app.handle(req('/valid')).then((x) => x.json())

expect(invalid).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(invalid).toBe(500)
expect(valid).toEqual({
foo: 'bar'
})
Expand Down
3 changes: 2 additions & 1 deletion test/core/handle-error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,8 @@ describe('Handle Error', () => {

const res = await app.handle(req('/'))

expect(res.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(res.status).toBe(500)
expect(res.headers.get('set-cookie')).toContain('session=test-session-id')
})

Expand Down
9 changes: 6 additions & 3 deletions test/core/normalize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ describe('Normalize', () => {

const response = await app.handle(req('/'))

expect(response.status).toEqual(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(response.status).toEqual(500)
})

it('normalize multiple response', async () => {
Expand Down Expand Up @@ -117,7 +118,8 @@ describe('Normalize', () => {

const response = await app.handle(req('/'))

expect(response.status).toEqual(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(response.status).toEqual(500)
})

it('normalize multiple response using 200', async () => {
Expand Down Expand Up @@ -171,7 +173,8 @@ describe('Normalize', () => {

const response = await app.handle(req('/'))

expect(response.status).toEqual(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(response.status).toEqual(500)
})

it('do not normalize response when allowing additional properties', async () => {
Expand Down
3 changes: 2 additions & 1 deletion test/extends/error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ describe('Error', () => {

const response = await app.handle(req('/'))

expect(response.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(response.status).toBe(500)
expect(response.headers.get('content-type')).toBe('application/json')
})

Expand Down
6 changes: 4 additions & 2 deletions test/extends/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,8 @@ describe('Model', () => {
.get('/error', () => 1, { response: 'res' })

const error = await app.handle(req('/error'))
expect(error.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(error.status).toBe(500)

const correct = await app.handle(req('/correct'))
expect(correct.status).toBe(200)
Expand Down Expand Up @@ -347,7 +348,8 @@ describe('Model', () => {
})

const error = await app.handle(req('/error'))
expect(error.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(error.status).toBe(500)

const correct = await app.handle(req('/correct'))
expect(correct.status).toBe(200)
Expand Down
3 changes: 2 additions & 1 deletion test/path/group.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ describe('group', () => {
const correct = await app.handle(req('/v1/correct'))

expect(correct.status).toBe(200)
expect(error.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(error.status).toBe(500)
})

it('validate request with prefix', async () => {
Expand Down
24 changes: 16 additions & 8 deletions test/path/guard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ describe('guard', () => {
const correct = await app.handle(req('/correct'))

expect(correct.status).toBe(200)
expect(error.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(error.status).toBe(500)
})

it('apply guard globally', async () => {
Expand All @@ -168,7 +169,8 @@ describe('guard', () => {
const correct = await app.handle(req('/correct'))

expect(correct.status).toBe(200)
expect(error.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(error.status).toBe(500)
})

it('inherits singleton / definitions and re-meregd on main', async () => {
Expand Down Expand Up @@ -238,7 +240,8 @@ describe('guard', () => {
])

expect(called).toBe(3)
expect(response).toEqual([422, 422, 422])
// Response validation errors return 500 (server error) - see issue #1480
expect(response).toEqual([500, 500, 500])
})

it('handle as global with local override', async () => {
Expand Down Expand Up @@ -275,7 +278,8 @@ describe('guard', () => {
])

expect(called).toBe(4)
expect(response).toEqual([422, 200, 422])
// Response validation errors return 500 (server error) - see issue #1480
expect(response).toEqual([500, 200, 500])
})

it('handle as global with scoped override', async () => {
Expand Down Expand Up @@ -312,7 +316,8 @@ describe('guard', () => {
])

expect(called).toBe(5)
expect(response).toEqual([422, 200, 200])
// Response validation errors return 500 (server error) - see issue #1480
expect(response).toEqual([500, 200, 200])
})

it('handle as scoped', async () => {
Expand Down Expand Up @@ -343,7 +348,8 @@ describe('guard', () => {
])

expect(called).toBe(2)
expect(response).toEqual([422, 422, 200])
// Response validation errors return 500 (server error) - see issue #1480
expect(response).toEqual([500, 500, 200])
})

it('handle as local', async () => {
Expand Down Expand Up @@ -371,7 +377,8 @@ describe('guard', () => {
])

expect(called).toBe(1)
expect(response).toEqual([422, 200, 200])
// Response validation errors return 500 (server error) - see issue #1480
expect(response).toEqual([500, 200, 200])
})

it('only cast guard', async () => {
Expand Down Expand Up @@ -399,7 +406,8 @@ describe('guard', () => {
])

expect(called).toBe(3)
expect(response).toEqual([422, 200])
// Response validation errors return 500 (server error) - see issue #1480
expect(response).toEqual([500, 200])
})

it('handle merge guard and hook on non-specified responses status', () => {
Expand Down
6 changes: 4 additions & 2 deletions test/standard-schema/reference.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ describe('Standard Schema Validate', () => {
const nonExists = await app.handle(req('/lilith'))

expect(exists.status).toBe(200)
expect(nonExists.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(nonExists.status).toBe(500)
})

it('validate multiple response', async () => {
Expand Down Expand Up @@ -151,7 +152,8 @@ describe('Standard Schema Validate', () => {
expect(nonExists.status).toBe(404)

const invalid = await app.handle(req('/unknown'))
expect(invalid.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(invalid.status).toBe(500)
})

it('validate multiple schema together', async () => {
Expand Down
9 changes: 6 additions & 3 deletions test/standard-schema/standalone.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ describe('Standard Schema Standalone', () => {
expect(valid).toEqual({ id: 1, name: 'lilith' })

const invalid = await app.handle(req('/focou'))
expect(invalid.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(invalid.status).toBe(500)
})

it('validate and normalize multiple response', async () => {
Expand Down Expand Up @@ -208,7 +209,8 @@ describe('Standard Schema Standalone', () => {
expect(fouco).toEqual({ id: 2, name: 'fouco' })

const invalid = await app.handle(req('/unknown'))
expect(invalid.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(invalid.status).toBe(500)
})

it('validate multiple schema together', async () => {
Expand Down Expand Up @@ -354,7 +356,8 @@ describe('Standard Schema Standalone', () => {
expect(fouco).toEqual({ id: 2, name: 'fouco' })

const invalid = await app.handle(req('/unknown'))
expect(invalid.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(invalid.status).toBe(500)
})

it('validate non-typebox schema', async () => {
Expand Down
6 changes: 4 additions & 2 deletions test/standard-schema/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ describe('Standard Schema Validate', () => {
const nonExists = await app.handle(req('/lilith'))

expect(exists.status).toBe(200)
expect(nonExists.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(nonExists.status).toBe(500)
})

it('validate multiple response', async () => {
Expand All @@ -125,7 +126,8 @@ describe('Standard Schema Validate', () => {
expect(nonExists.status).toBe(404)

const invalid = await app.handle(req('/unknown'))
expect(invalid.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(invalid.status).toBe(500)
})

it('validate multiple schema together', async () => {
Expand Down
9 changes: 6 additions & 3 deletions test/validator/novalidate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ describe('ElysiaType.NoValidate', () => {

const res = await app.handle(req('/'))

expect(res.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(res.status).toBe(500)
})

it('should work with NoValidate on nested object properties', async () => {
Expand Down Expand Up @@ -248,7 +249,8 @@ describe('ElysiaType.NoValidate', () => {

const res = await app.handle(req('/'))

expect(res.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(res.status).toBe(500)
})

it('should validate normally with strict object schemas', async () => {
Expand All @@ -263,7 +265,8 @@ describe('ElysiaType.NoValidate', () => {

const res = await app.handle(req('/'))

expect(res.status).toBe(422)
// Response validation errors return 500 (server error) - see issue #1480
expect(res.status).toBe(500)
})

it('should handle null values with NoValidate', async () => {
Expand Down
Loading
Loading