Skip to content

fix(form-core): reset stale validation counter when shifting array meta#2247

Open
xianjianlf2 wants to merge 1 commit into
TanStack:mainfrom
xianjianlf2:fix/array-removevalue-isvalidating-stuck-2234
Open

fix(form-core): reset stale validation counter when shifting array meta#2247
xianjianlf2 wants to merge 1 commit into
TanStack:mainfrom
xianjianlf2:fix/array-removevalue-isvalidating-stuck-2234

Conversation

@xianjianlf2

@xianjianlf2 xianjianlf2 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

🎯 Changes

Fixes #2234. When an array item is removed (or inserted/moved) while an async validator is in flight, shiftMeta copied the source field's transient _pendingValidationsCount/isValidating down to the shifted index. The pending promise is owned by the original field instance and settles its counter against the original index, so the shifted index's counter is orphaned and never decremented — leaving form.state.isFieldsValidating stuck true and blocking submission.

  • shiftMeta now resets the transient validation state (isValidating, _pendingValidationsCount) when shifting meta between indices. Array mutations already re-trigger validation on the shifted fields, so this is safe.
  • endValidation bails out if the field's instance is no longer registered, so a validation that resolves after its field was removed no longer resurrects deleted meta (previously threw a TypeError).

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed async validation state remaining active after removing an item from an array field.
    • Ensured validation indicators and form-level validation status reset correctly when array fields shift during pending validation.
  • Tests

    • Added coverage for removing array items while asynchronous validation is in progress.

When an array item is removed (or inserted/moved) while an async validator
is in flight, `shiftMeta` copied the source field's transient
`_pendingValidationsCount`/`isValidating` down to the shifted index. The
pending promise is owned by the original field instance and settles its
counter against the original index, so the shifted index's counter is
orphaned and never decremented, leaving `isFieldsValidating` stuck `true`.

Reset the transient validation state when shifting meta, and guard
`endValidation` so a validation that resolves after its field was removed
no longer resurrects deleted meta.

Closes TanStack#2234
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change fixes stale async-validation state after removing array values by guarding validation completion for inactive fields and resetting transient metadata during index shifts. A regression test covers the behavior, and a patch Changesets entry documents the fix.

Changes

Array async-validation cleanup

Layer / File(s) Summary
Validation lifecycle and metadata shifting
packages/form-core/src/FieldApi.ts, packages/form-core/src/metaHelper.ts
Inactive field instances no longer update metadata after async validation completes, and shifted array fields reset isValidating and _pendingValidationsCount.
Regression coverage and release metadata
packages/form-core/tests/FieldApi.spec.ts, .changeset/fix-array-removevalue-isvalidating-stuck.md
A test reproduces removal during pending async validation and verifies field- and form-level state cleanup; a patch release entry documents the fix.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

Possibly related PRs

  • TanStack/form#2143 — Also updates async validation cleanup and isValidating resolution in FieldApi.ts.

Suggested reviewers: crutchcorn

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main fix to array meta validation state during shifting.
Description check ✅ Passed The description follows the template and covers changes, checklist, and release impact with the needed details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/form-core/src/metaHelper.ts (1)

100-116: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reset async-validation state in handleArrayMove/handleArraySwap. Reapplying fromMeta and swapping meta verbatim carries isValidating and _pendingValidationsCount to the new index, so an in-flight validation can stay stuck on the wrong field. Mirror shiftMeta’s reset here and add a move/swap regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/form-core/src/metaHelper.ts` around lines 100 - 116, Reset
async-validation state when restoring metadata in handleArrayMove and
handleArraySwap, matching shiftMeta’s reset behavior instead of copying
isValidating and _pendingValidationsCount unchanged. Update the metadata
transfer logic around fromMeta and swapped metadata, then add regression
coverage verifying moved and swapped fields do not retain in-flight validation
state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/form-core/src/metaHelper.ts`:
- Around line 100-116: Reset async-validation state when restoring metadata in
handleArrayMove and handleArraySwap, matching shiftMeta’s reset behavior instead
of copying isValidating and _pendingValidationsCount unchanged. Update the
metadata transfer logic around fromMeta and swapped metadata, then add
regression coverage verifying moved and swapped fields do not retain in-flight
validation state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 48e8bfdf-c589-40d8-979e-008a9b920059

📥 Commits

Reviewing files that changed from the base of the PR and between 5d11281 and 351d752.

📒 Files selected for processing (4)
  • .changeset/fix-array-removevalue-isvalidating-stuck.md
  • packages/form-core/src/FieldApi.ts
  • packages/form-core/src/metaHelper.ts
  • packages/form-core/tests/FieldApi.spec.ts

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.

Using onChangeAsync in an array field causes isFieldsValidating to always be true after calling removeValue

1 participant