Summary
In typebox@1.3.33, error paths concatenate property names without JSON Pointer escaping. A literal key "a/b" and a nested a.b both produce instancePath: "/a/b". A literal "a~1b" key produces /a~1b, which decodes to the different key "a/b".
Validation correctly rejects the values. The problem is the location reported by the diagnostics: even TypeBox's own Value.Pointer.Get(value, error.instancePath) retrieves the wrong value. schemaPath has the same escaping problem.
Self-contained reproduction
Tested with Node.js 24.21.0 on macOS arm64, using a fresh installation of typebox@1.3.33 (the typebox package, not @sinclair/typebox). No settings or schema transforms are involved.
mkdir typebox-pointer-repro
cd typebox-pointer-repro
npm init -y
npm install --save-exact typebox@1.3.33
Save this as repro.mjs, then run node repro.mjs:
import assert from 'node:assert/strict'
import Type from 'typebox'
import Value from 'typebox/value'
const schema = Type.Object({
'a/b': Type.Number(),
a: Type.Object({ b: Type.Number() }),
'a~1b': Type.Number()
})
const value = { 'a/b': 'literal slash', a: { b: 'nested' }, 'a~1b': 'literal tilde' }
const errors = Value.Errors(schema, value)
console.log(JSON.stringify(errors.map(error => ({
instancePath: error.instancePath,
schemaPath: error.schemaPath,
resolvedValue: Value.Pointer.Get(value, error.instancePath)
})), null, 2))
assert.deepEqual(errors.map(error => error.instancePath), ['/a~1b', '/a/b', '/a~01b'])
Actual output
[
{
"instancePath": "/a/b",
"schemaPath": "#/properties/a/b",
"resolvedValue": "nested"
},
{
"instancePath": "/a/b",
"schemaPath": "#/properties/a/properties/b",
"resolvedValue": "nested"
},
{
"instancePath": "/a~1b",
"schemaPath": "#/properties/a~1b",
"resolvedValue": "literal slash"
}
]
The assertion fails with actual paths ['/a/b', '/a/b', '/a~1b'].
Expected output
[
{
"instancePath": "/a~1b",
"schemaPath": "#/properties/a~1b",
"resolvedValue": "literal slash"
},
{
"instancePath": "/a/b",
"schemaPath": "#/properties/a/properties/b",
"resolvedValue": "nested"
},
{
"instancePath": "/a~01b",
"schemaPath": "#/properties/a~01b",
"resolvedValue": "literal tilde"
}
]
Per RFC 6901 §3, property-name tokens must encode ~ as ~0 and / as ~1, while preserving the separators between tokens. This is important for assigning errors to fields and resolving their values or schemas.
Source verification
Also reproduced directly from a fresh clone at 055e51c with Deno 2.7.14. Focused tests cover instancePath, schemaPath, and pointer round-tripping in both Value.Errors(schema, value) and Compile(schema).Errors(value); all six diagnostic assertions fail, while valid/invalid boolean validation checks pass. The existing three Value.Errors tests also pass.
The relevant construction is in ErrorProperties, which inserts the raw key into both paths.
Related history: #643 reported the equivalent ambiguity in the older API and was reported fixed in 0.31.19. This report concerns the current 1.3.33 package and its instancePath / schemaPath diagnostics.
Summary
In
typebox@1.3.33, error paths concatenate property names without JSON Pointer escaping. A literal key"a/b"and a nesteda.bboth produceinstancePath: "/a/b". A literal"a~1b"key produces/a~1b, which decodes to the different key"a/b".Validation correctly rejects the values. The problem is the location reported by the diagnostics: even TypeBox's own
Value.Pointer.Get(value, error.instancePath)retrieves the wrong value.schemaPathhas the same escaping problem.Self-contained reproduction
Tested with Node.js 24.21.0 on macOS arm64, using a fresh installation of
typebox@1.3.33(thetypeboxpackage, not@sinclair/typebox). No settings or schema transforms are involved.mkdir typebox-pointer-repro cd typebox-pointer-repro npm init -y npm install --save-exact typebox@1.3.33Save this as
repro.mjs, then runnode repro.mjs:Actual output
[ { "instancePath": "/a/b", "schemaPath": "#/properties/a/b", "resolvedValue": "nested" }, { "instancePath": "/a/b", "schemaPath": "#/properties/a/properties/b", "resolvedValue": "nested" }, { "instancePath": "/a~1b", "schemaPath": "#/properties/a~1b", "resolvedValue": "literal slash" } ]The assertion fails with actual paths
['/a/b', '/a/b', '/a~1b'].Expected output
[ { "instancePath": "/a~1b", "schemaPath": "#/properties/a~1b", "resolvedValue": "literal slash" }, { "instancePath": "/a/b", "schemaPath": "#/properties/a/properties/b", "resolvedValue": "nested" }, { "instancePath": "/a~01b", "schemaPath": "#/properties/a~01b", "resolvedValue": "literal tilde" } ]Per RFC 6901 §3, property-name tokens must encode
~as~0and/as~1, while preserving the separators between tokens. This is important for assigning errors to fields and resolving their values or schemas.Source verification
Also reproduced directly from a fresh clone at 055e51c with Deno 2.7.14. Focused tests cover
instancePath,schemaPath, and pointer round-tripping in bothValue.Errors(schema, value)andCompile(schema).Errors(value); all six diagnostic assertions fail, while valid/invalid boolean validation checks pass. The existing threeValue.Errorstests also pass.The relevant construction is in
ErrorProperties, which inserts the rawkeyinto both paths.Related history: #643 reported the equivalent ambiguity in the older API and was reported fixed in 0.31.19. This report concerns the current 1.3.33 package and its
instancePath/schemaPathdiagnostics.