feat!: carry Standard Schema issues on InvalidEntity - #5
Merged
Conversation
Schema issues now render as "<path>: <message>" — dotted for nesting,
array indices as ordinary segments ("secret: Too small: …", "tags.0: …",
"address.city: …") — so a caller can tell which field failed and key a
field-level error response by splitting on the first ": ".
`issues` stays a readonly string[]: the path goes into the string rather
than alongside it, keeping one representation of an issue instead of two
parallel ones, and keeping the change non-breaking.
Whole-object issues (empty path) and `invariants` messages — domain
sentences about the entity, not field complaints — stay unprefixed.
btravers
force-pushed
the
feat/invalid-entity-issue-paths
branch
from
August 6, 2026 20:27
365a889 to
73c24e4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reworked per review. The previous approach flattened issues into
"field: message"strings; this keeps them structured.Why
fromSchemaalready returnsSchemaIssues(readonly StandardSchemaV1.Issue[])and
toInvalidEntitywas discarding it. Flattening was lossy — a message canitself contain
": ", so the path could not be recovered — and it forced acaller to re-parse a string the library had just serialised from data it held.
@unthrown/standard-schemais already a peer dependency, so adopting itsIssuetype costs nothing.It was also self-inconsistent:
instancealready propagated structured issuesinto zod's channel with real paths.
Changes
errors.ts—issues: SchemaIssuesinstead ofreadonly string[].entity.ts—toInvalidEntitypasses issues through untouched.constructwraps invariant messages as
{ message }with nopath.instance.ts— forwards each issue's ownpathintoctx.addIssue, so anested field failure now reports
["owner", "slug"]rather than just["owner"]. Two tests pin this.issues.ts(new) —keysOf(Standard Schema permits both barePropertyKeyand
{ key }segments; zod emits the bare form) andrenderIssue, usedonly for the
add-defectErrormessage, which has nowhere to putstructure.
Behaviour
The pathless-invariant case is new information, not just a different encoding:
a whole-entity rule failure is now distinguishable from a field issue that
happened to have an empty path.
Gate
format --check,lint,typecheck(both passes),test(86, 9 files),knip,build— all green. Rebased onto currentmain.