Skip to content

Pre-reset field validation completion clears a newer validation status #2384

Description

@lunaxislu

Describe the bug

A field validation started before form.reset() can clear the validation status of a newer run when the older run finishes. A UI reading field.state.meta.isValidating can stop displaying its checking indicator while the new validation is still pending.

This is a follow-up to the review discussion on #2382. The counter race reproduces on that PR's base as well as its head; it is separate from the stale-error fix in #2381/#2382.

Your minimal, reproducible example

Tests-only commit with unchanged main production code

git clone https://github.com/lunaxislu/form.git
cd form
git checkout ba6de4a83142fafcb74d5763f7e6574067019b14
# Use Node 24.8.0 from .nvmrc and pnpm via Corepack.
corepack pnpm install --frozen-lockfile
corepack pnpm --dir packages/form-core exec vitest run tests/FieldApi.spec.ts -t 'should preserve pending validation when an older run settles'

The four cases use deferred promises and fake timers. The two reset cases fail; the two no-reset controls pass. Both direct and linked-field validation are covered.

Steps to reproduce

  1. Start validation A via a field's onChangeAsync and leave its promise pending.
  2. Call form.reset().
  3. Blur the field to start validation B via onBlurAsync and leave B pending.
  4. Resolve A.
  5. Observe that the field's isValidating becomes false even though B is pending.
A starts       count=1, isValidating=true
reset          count=0, isValidating=false
B starts       count=1, isValidating=true
A settles      count=0, isValidating=false  <-- B is still pending
B settles      count=0, isValidating=false

Using different validation causes avoids the same-cause debounce timer issue tracked by #2372/#2373. The linked-field variant starts A by changing another field listed in onChangeListenTo.

Expected behavior

After reset, a completion belonging to a pre-reset run should not decrement the new run's counter. field.state.meta.isValidating should remain true until B settles. Without reset, overlapping runs should still decrement normally (2 → 1 → 0).

How often does this bug happen?

Every time with the controlled ordering above.

Platform

macOS; reproduced in the form-core Vitest suite with jsdom, without a browser UI or server dependency.

TanStack Form adapter

vanilla (@tanstack/form-core)

TanStack Form version

1.33.5, main source at 57a855b42b9b99cee5ca12890b70c7df84e2982c.

TypeScript version

5.9.3

Additional context

Reset replaces field metadata, including _pendingValidationsCount. The outer completion block of FieldApi.validateAsync still calls endValidation() against current metadata without distinguishing a pre-reset run. This applies to the main field and linked fields.

The report concerns completion accounting across a full form reset. It does not request changes to debounce scheduling, per-field reset, or form/group-level validation. Searches for reset/counter/isValidating and related reports did not reveal a separate report for this exact sequence beyond the #2382 review discussion.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions