v5: validateStandardSchema - inconsistent dot notation VS bracket notation in error.path
### What happened?
Sorry for not having more time to write a detailed bug report.
I am using `fieldArray` with object-like `field`s, represented by this zod schema:
```ts
const notificationChannelsSchema = z.array(z.object({
id: nonEmptyString('Notification channel ID is required.', 'Notification channel ID is invalid.'),
name: nonEmptyString('Notification channel name is required.', 'Notification channel name is invalid.'),
}))
```
I have a button which dynamically adds notification channel input elements with the following names:
* `notificationChannels[$idx].id`
* `notificationChannels[$idx].name`
I use validate on mount, but when the field gets mounted, `errors` object from `useFormContext` contains only the error message for the `name` field, and not for the `id` field, even though both of them are initially empty and required.
I asked Opus 4.5 to add debug logs to vee-validate.mjs and it discovered a discrepancy beteween dot notation and bracked notation in error paths (`id` field gets dot notation, `name` field gets bracket notation). It has something to do with debouncing and `withLatest` function and `onDone` callback. I don't have time to dig deeper into that.
However, it suggested the fix which works great:
### Working fix recommended by Opus 4.5
```diff
diff --git a/dist/vee-validate.mjs b/dist/vee-validate.mjs
index 1234567..abcdefg 100644
--- a/dist/vee-validate.mjs
+++ b/dist/vee-validate.mjs
@@ -1003,7 +1003,9 @@ async function validateStandardSchema(schema, values) {
const combinedIssues = combineStandardIssues(validationResult.issues || []);
for (const error of combinedIssues) {
const messages = error.messages;
- const path = error.path;
+ // FIX: Normalize path from dot notation (notificationChannels.0.id) to bracket notation (notificationChannels[0].id)
+ // This ensures consistency with how fields are registered via useField
+ const path = normalizeFormPath(error.path);
results[path] = { valid: !messages.length, errors: messages };
if (messages.length) {
errors[path] = messages[0];
```
### Short explanation from AI
#### The Problem with withLatest
`withLatest` is designed to only run `onDone` once for the last caller:
```
function withLatest(fn, onDone) {
let latestRun; // Single shared variable
return async function runLatest(...args) {
const pending = fn(...args); // Creates a NEW promise
latestRun = pending; // Overwrites previous
const result = await pending;
if (pending !== latestRun) {
return result; // ❌ Earlier caller - NO transformation
}
return onDone(result, args); // ✅ Latest caller - WITH transformation
};
}
```
**Why both don't get bracket notation:**
1. .id calls validateSchema() → creates promise_A, sets latestRun = promise_A
2. .name calls validateSchema() → creates promise_B, sets latestRun = promise_B (overwrites!)
3. Both promises resolve with the same raw data (dot notation from schema)
4. .id's promise checks: promise_A !== latestRun (it's promise_B now) → returns RAW (dot)
5. .name's promise checks: promise_B === latestRun → runs onDone → returns transformed (bracket)
**The design flaw:**
withLatest assumes onDone has side effects that should only happen once (like setting errors on all fields). But it also transforms the return value, which means earlier callers get untransformed data.
This is why my fix normalizes paths inside validateStandardSchema (before withLatest sees it), so the raw data already has bracket notation - then it doesn't matter if onDone runs or not.
**TL;DR:** withLatest intentionally only transforms the last caller's result. Earlier callers get raw data. That's the bug when the transformation includes path normalization.
### Reproduction steps
1.
2.
3.
...
### Version
Vue.js 3.x and vee-validate 4.x
### What browsers are you seeing the problem on?
- [ ] Firefox
- [ ] Chrome
- [ ] Safari
- [ ] Microsoft Edge
### Relevant log output
```shell
```
### Demo link
.
### Code of Conduct
- [x] I agree to follow this project's [Code of Conduct](CONDUCT.md)
0 条评论