Skip to content

Fix: Ensure validation errors are sorted by schema insertion order #2281

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

severikupari1
Copy link

Summary

This PR addresses #2279 by ensuring that validation errors are returned in the same order as fields are defined in the schema — particularly when multiple nested objects exist at the same level. Previously, validation errors could appear in reverse or unpredictable order due to internal traversal logic.

Problem

When validating schemas with abortEarly: false, Yup collects errors from nested objects but does not guarantee that those errors respect the declaration order within the schema. For example:

const schema = yup.object({
  testObjects: yup.object().shape({
    object: yup.object({ field: yup.string().required() }),
    objectC: yup.object({ field: yup.string().required() }),
    objectB: yup.object({ field: yup.string().required() }),
  }),
})

Produces this incorrect error order:

[
  { "path": "testObjects.objectB.field", "message": "..." },
  { "path": "testObjects.objectC.field", "message": "..." },
  { "path": "testObjects.object.field", "message": "..." }
]

Fix

  • This PR implements the following:
  • Recursively collects schema field paths (dot notation) in declaration order.
  • Adds inline logic in runTests() to dynamically sort nestedErrors using a comparator built from those field paths.
  • Introduces a depth limit (default 25) to prevent excessive or infinite recursion.
  • Uses a WeakMap to cache paths per schema for performance.

Highlights

  • No changes to schema APIs (_sortErrors or options) — logic is entirely contained within runTests().

No breaking changes.

Inline and scoped: only affects the error collection phase when abortEarly: false.

Example After Fix

[
  { "path": "testObjects.object.field", "message": "..." },
  { "path": "testObjects.objectB.field", "message": "..." },
  { "path": "testObjects.objectC.field", "message": "..." }
]

Closes

#2279

Let me know if you'd like to include benchmarks or add a test case into the PR body for reviewers — happy to help tighten it up more!

@jquense
Copy link
Owner

jquense commented Apr 4, 2025

hey there, thanks for attempting to fix this. The solution here is too complex for a simple sort order convenience. The bug is just in the findIndex function in the existing code. We just need to fix it so it's smarter than looking at specific substrings

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.

2 participants