Skip to content

fix(schema): correct hasElysiaMeta recursive logic to prevent short-c…#1787

Open
NongTham wants to merge 2 commits intoelysiajs:mainfrom
NongTham:fix-1783-hasElysiaMeta
Open

fix(schema): correct hasElysiaMeta recursive logic to prevent short-c…#1787
NongTham wants to merge 2 commits intoelysiajs:mainfrom
NongTham:fix-1783-hasElysiaMeta

Conversation

@NongTham
Copy link
Contributor

@NongTham NongTham commented Mar 7, 2026

This PR fixes a bug in hasElysiaMeta introduced in #1649 where the recursive search loop was unintentionally short-circuiting on the first property checked.

Changes:

  • Moved the return statement outside the for loop in hasElysiaMeta to allow deep traversal.
  • Added comprehensive unit tests in schema-utils.test.ts to ensure hasElysiaMeta correctly finds nested metadata in Object and Union schemas without breaking.

(Note: The t.Union behavior originally reported in #1783 dropping subsequent schema properties is determined to be expected TypeBox matching behavior under Elysia's root-only additionalProperties normalization. This PR solely addresses the loop defect preventing meta tagging.)

Summary by CodeRabbit

  • Bug Fixes

    • Improved object-schema evaluation so property metadata and additional-property checks are fully inspected before returning results, leading to more accurate validation outcomes.
    • Standardized error extraction to provide more consistent error paths.
  • Tests

    • Added tests covering metadata detection in nested and reordered object properties.
  • Chores

    • Minor internal formatting and consistency cleanups.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2026

Walkthrough

Reworked traversal logic in schema utilities: hasAdditionalProperties and hasElysiaMeta now iterate all object properties before returning, removing per-property early returns. Standardized some catch blocks and error-field references; added tests for hasElysiaMeta covering order and deep nesting.

Changes

Cohort / File(s) Summary
Schema logic
src/schema.ts
Rewrote property traversal in hasAdditionalProperties and hasElysiaMeta to evaluate all properties instead of early-returning; replaced compiled.Errors with validator.Errors in error paths; standardized catch formatting and minor renames/whitespace changes. No exported signatures changed.
Tests
test/schema/schema-utils.test.ts
Added tests for hasElysiaMeta to ensure detection across property order, deep nested ObjectString structures, and absence cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I nibble through each property, one by one,
No early hops until the check is done.
Deep nests and late leaves, I won’t miss a clue,
I find the meta — hooray! — and then I chew. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main bug fix: correcting hasElysiaMeta recursive logic to prevent short-circuiting, which directly aligns with the PR's primary objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 7, 2026

Open in StackBlitz

npm i https://pkg.pr.new/elysia@1787

commit: 1030965

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/schema.ts (2)

231-242: ⚠️ Potential issue | 🟠 Major

Mirror this traversal fix in hasAdditionalProperties().

hasElysiaMeta() is fixed here, but Line 128 in hasAdditionalProperties() still returns from inside the first property iteration. A schema whose first property is plain and whose second property carries nested additionalProperties will still be misclassified, so the same short-circuit bug remains reachable through validator.hasAdditionalProperties.

🛠 Proposed fix
 		for (const key of Object.keys(properties)) {
 			const property = properties[key]

 			if (property.type === 'object') {
 				if (hasAdditionalProperties(property)) return true
 			} else if (property.anyOf) {
 				for (let i = 0; i < property.anyOf.length; i++)
 					if (hasAdditionalProperties(property.anyOf[i])) return true
 			}
-
-			return property.additionalProperties
+			if (property.additionalProperties) return true
 		}

 		return false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/schema.ts` around lines 231 - 242, The traversal bug fixed in
hasElysiaMeta must be mirrored in hasAdditionalProperties: do not return from
inside the first property iteration — instead, when schema.type === 'object' and
schema.properties exists, iterate every property (using the same pattern as
hasElysiaMeta), check each property's nested schema for additionalProperties
(and recurse into hasAdditionalProperties as needed), and only return true if
any property ultimately indicates additionalProperties; otherwise fall through
to return schema.additionalProperties === true (or existing final check). Update
hasAdditionalProperties to use the same non-short-circuit loop and recursive
checks as hasElysiaMeta to correctly detect nested additionalProperties across
all properties.

778-877: ⚠️ Potential issue | 🔴 Critical

safeParse() in the else branch throws ReferenceError instead of returning error response.

The safeParse() method in the else branch (lines ~879-950) references compiled.Errors(v) in its catch block, but compiled is a let variable declared after the entire if-else block. When the catch block executes before compiled is initialized, it throws ReferenceError: Cannot access 'compiled' before initialization instead of returning { success: false, ... }.

🛠 Fix
 		validator.safeParse = (v) => {
 			try {
 				return {
 					success: true,
 					data: validator.Decode(validator.Clean?.(v) ?? v),
 					error: null
 				}
 			} catch (error) {
-				const errors = [...compiled.Errors(v)].map(mapValueError)
+				const errors = [...validator.Errors(v)].map(mapValueError)

 				return {
 					success: false,
 					data: null,
 					error: errors[0]?.summary,
 					errors
 				}
 			}
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/schema.ts` around lines 778 - 877, The safeParse() catch block references
compiled.Errors(v) which can trigger a ReferenceError because the compiled
variable is declared later; update safeParse to use the already-defined
validator.Errors(v) (or another initialized symbol like Errors or validator)
instead of compiled, or move the compiled declaration so it is initialized
before safeParse is defined; specifically change safeParse's catch to compute
errors from validator.Errors(v) and map via mapValueError to return the error
shape without relying on compiled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/schema.ts`:
- Around line 231-242: The traversal bug fixed in hasElysiaMeta must be mirrored
in hasAdditionalProperties: do not return from inside the first property
iteration — instead, when schema.type === 'object' and schema.properties exists,
iterate every property (using the same pattern as hasElysiaMeta), check each
property's nested schema for additionalProperties (and recurse into
hasAdditionalProperties as needed), and only return true if any property
ultimately indicates additionalProperties; otherwise fall through to return
schema.additionalProperties === true (or existing final check). Update
hasAdditionalProperties to use the same non-short-circuit loop and recursive
checks as hasElysiaMeta to correctly detect nested additionalProperties across
all properties.
- Around line 778-877: The safeParse() catch block references compiled.Errors(v)
which can trigger a ReferenceError because the compiled variable is declared
later; update safeParse to use the already-defined validator.Errors(v) (or
another initialized symbol like Errors or validator) instead of compiled, or
move the compiled declaration so it is initialized before safeParse is defined;
specifically change safeParse's catch to compute errors from validator.Errors(v)
and map via mapValueError to return the error shape without relying on compiled.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 881e15e9-e7bd-4aa3-b9da-e26be9deb3f9

📥 Commits

Reviewing files that changed from the base of the PR and between 169e54a and 0a70c46.

📒 Files selected for processing (2)
  • src/schema.ts
  • test/schema/schema-utils.test.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/schema.ts (1)

226-233: Loop fix is correct, but the final return is redundant.

The traversal fix at line 229 properly ensures all properties are checked recursively. However, line 232 return schema.elysiaMeta === meta is redundant since schema.elysiaMeta === meta was already checked at line 200 - if it were true, the function would have returned there. At line 232, this will always evaluate to false.

This isn't a bug, but could be simplified for clarity:

♻️ Optional: Simplify the return statement
 		for (const key of Object.keys(properties)) {
 			const property = properties[key]

 			if (hasElysiaMeta(meta, property)) return true
 		}

-		return schema.elysiaMeta === meta
+		return false
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/schema.ts` around lines 226 - 233, The final return in hasElysiaMeta is
redundant because schema.elysiaMeta was already checked earlier; remove the
trailing "return schema.elysiaMeta === meta" (or replace it with "return false")
so the function returns true only when found during the earlier checks/recursive
loop and otherwise returns false at the end; update the hasElysiaMeta function
to end with an explicit false return if needed and ensure no other logic depends
on that redundant comparison.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/schema.ts`:
- Around line 226-233: The final return in hasElysiaMeta is redundant because
schema.elysiaMeta was already checked earlier; remove the trailing "return
schema.elysiaMeta === meta" (or replace it with "return false") so the function
returns true only when found during the earlier checks/recursive loop and
otherwise returns false at the end; update the hasElysiaMeta function to end
with an explicit false return if needed and ensure no other logic depends on
that redundant comparison.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ddb6dbbf-ffc6-4ce9-a3c1-d577a1f0d473

📥 Commits

Reviewing files that changed from the base of the PR and between 0a70c46 and 7f7315a.

📒 Files selected for processing (1)
  • src/schema.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant