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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 1.4.22
Bug fix:
- ValueError with summary missing types

# 1.4.21 - 4 Jan 2026
Improvement:
- [#1654](https://github.com/elysiajs/elysia/pull/1654) encode t.Date() to iso string
Expand Down
55 changes: 42 additions & 13 deletions example/a.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,43 @@
import { Elysia, t } from '../src'
import { z } from 'zod'
import { req } from '../test/utils'

export const app = new Elysia()
.ws('/', {
open(ws) {
ws.subscribe('a')
},
message(a) {
console.log(a.subscriptions)
}
})
.listen(3000)

export const errorHandler = new Elysia({ name: "error-handler" })
.onError(({ code, error, status }) => {
switch (code) {
case "VALIDATION": {
const allErrors = error.detail

return status(422, {
status: 422,
message: error.valueError?.message,
details: {
location: error.type,
rejected_value: error.value,
expected: error.expected,
},
})
Comment on lines +6 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

error.detail is a method, not a property—and allErrors is unused.

detail() in ValidationError is a method requiring parameters (message, allowUnsafeValidatorDetails). Line 7 assigns the method reference itself (not its result) to allErrors, and the variable is never used.

Either remove the dead code or call the method properly:

🔎 Option 1: Remove unused code
 case "VALIDATION": {
-	const allErrors = error.detail
-
 	return status(422, {
🔎 Option 2: If you intended to get all errors
 case "VALIDATION": {
-	const allErrors = error.detail
+	const allErrors = error.all  // Use the getter for all validation errors

 	return status(422, {
 		status: 422,
 		message: error.valueError?.message,
 		details: {
 			location: error.type,
 			rejected_value: error.value,
 			expected: error.expected,
+			errors: allErrors,
 		},
 	})
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case "VALIDATION": {
const allErrors = error.detail
return status(422, {
status: 422,
message: error.valueError?.message,
details: {
location: error.type,
rejected_value: error.value,
expected: error.expected,
},
})
case "VALIDATION": {
return status(422, {
status: 422,
message: error.valueError?.message,
details: {
location: error.type,
rejected_value: error.value,
expected: error.expected,
},
})
}
🤖 Prompt for AI Agents
In example/a.ts around lines 6 to 17, the code assigns error.detail (a method)
to allErrors and never uses it; either remove the unused assignment or call
detail(...) correctly and use its result. If you meant to discard it, delete the
const allErrors = error.detail line. If you meant to retrieve all validation
errors, call error.detail(message?, allowUnsafeValidatorDetails?) with
appropriate args, assign the returned value to a variable, and include that
value in the response (e.g., replace the current details payload or add an
errors field). Ensure the call matches the ValidationError.detail signature and
that the returned data is used instead of leaving a dead variable.

}
case "NOT_FOUND": {
return status(404, {
status: 404,
message: "Route not found",
})
}
default: {
const statusCode =
"status" in error
? (typeof error.status === "number" ? error.status : Number(error.status)) || 500
: 500

let errMsg = "An error occurred"
if (error instanceof Error && error.message) {
errMsg = error.message
}

return status(statusCode, {
status: statusCode,
message: errMsg,
})
}
}
})
.as("scoped")
35 changes: 13 additions & 22 deletions src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ type CheckExcessProps<T, U> = 0 extends 1 & T
: U extends U
? Exclude<keyof T, keyof U> extends never
? T
: { [K in keyof U]: U[K] } & { [K in Exclude<keyof T, keyof U>]: never }
: { [K in keyof U]: U[K] } & {
[K in Exclude<keyof T, keyof U>]: never
}
: never

export type SelectiveStatus<in out Res> = <
Expand Down Expand Up @@ -154,27 +156,14 @@ export class InvalidCookieSignature extends Error {
}
}

type MapValueError =
| {
summary: undefined
}
| {
summary: string
}
| Prettify<
{
summary: string
} & ValueError
>

export const mapValueError = (error: ValueError | undefined): MapValueError => {
if (!error)
return {
summary: undefined
}
interface ValueErrorWithSummary extends ValueError {
summary?: string
}

let { message, path, value, type } = error
export const mapValueError = (error: ValueError): ValueErrorWithSummary => {
if (!error) return error

let { message, path, value, type } = error
Comment on lines +163 to +166
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Type signature inconsistency with runtime guard.

The parameter is typed as ValueError (non-nullable), but line 164 guards against falsy values and returns error (potentially undefined). This creates a mismatch: the return type is ValueErrorWithSummary, but the function can actually return undefined.

Consider either:

  1. Updating the signature to reflect nullable input/output: (error: ValueError | undefined): ValueErrorWithSummary | undefined
  2. Removing the guard if callers are guaranteed to pass non-null values
🔎 Option 1: Reflect nullable types
-export const mapValueError = (error: ValueError): ValueErrorWithSummary => {
-	if (!error) return error
+export const mapValueError = (error: ValueError | undefined): ValueErrorWithSummary | undefined => {
+	if (!error) return undefined
🤖 Prompt for AI Agents
In src/error.ts around lines 163 to 166, the function mapValueError is typed to
accept a non-nullable ValueError but contains a runtime guard that returns the
(possibly undefined) error, causing a signature mismatch; update the function
signature to accept and return nullable types like (error: ValueError |
undefined): ValueErrorWithSummary | undefined, or if callers never pass
undefined, remove the guard and keep the non-nullable signature—choose one
approach and adjust the type annotations accordingly so runtime behavior matches
the TypeScript types.

if (Array.isArray(path)) path = path[0]

const property =
Expand Down Expand Up @@ -495,7 +484,7 @@ export class ValidationError extends Error {
Object.setPrototypeOf(this, ValidationError.prototype)
}

get all(): MapValueError[] {
get all(): ValueErrorWithSummary[] {
// Handle standard schema validators (Zod, Valibot, etc.)
if (
// @ts-ignore
Expand Down Expand Up @@ -607,7 +596,9 @@ export class ValidationError extends Error {
on: this.type,
property: this.valueError?.path || 'root',
message,
summary: mapValueError(this.valueError).summary,
summary: this.valueError
? mapValueError(this.valueError).summary
: undefined,
found: value,
expected,
errors
Expand Down
Loading