TanStack / TanStack/form

_handleSubmit gates form-level validation behind field-level validity, causing incomplete error reports on submit

Open
#2,130 2 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

v1 v2: needs investigation
Dominant language
TypeScript
Stars
6.7k
Forks
682
Avg merge
5d 18h
Merged PRs (30d)
7

Description

Describe the bug

I believe this is a bug, though I want to flag upfront that the staging in _handleSubmit seems to be intentional. Regardless I'd like to argue the current behavior still isn't right.

_handleSubmit runs validation in two stages with an early return between them:

await this.validateAllFields('submit')             // Stage 1: field-level validators
if (!this.state.isFieldsValid){ /* ... */ return } // Gate
await this.validate('submit')                      // Stage 2: form-level validators

If Stage 1 fails, users only see the errors from this stage. So any other invalid fields stay silent until these errors are fixed. This contradicts what users intuitively expect from a submit action: check everything and show all errors.

Setting canSubmitWhenInvalid: true also doesn't fix this. The flag gates whether _handleSubmit runs at all, not how validation is staged within it.


My use case:

I only use form-level validation and canSubmitWhenInvalid: true.

Form-level validators write their errors into individual fields' errorMap. But isFieldsValid reads those entries without distinguishing source. So after a failed submit, those form-level errors count as "field-level invalid" on the next attempt — the isFieldsValid gate triggers, form-level validation is skipped, and stale errors stay on screen while new ones never appear.


This mechanism appears to be the underlying cause / relevant for several issues that have been reported separately: #1874, #1663 (see TeChn4K's reproduction there), #2034

PR #2120 in response to #2034 addresses a narrower symptom and doesn't solve the core issue: In #2034 the user has an onBlur validation on a field with conditional validation logic based on another field. The reported issue can be fixed by linking the other field with onBlurListenTo or using canSubmitWhenInvalid: true. The PR does not touch the isFieldsValid gate, so it does not address the problems above.

Your minimal, reproducible example

https://codesandbox.io/p/sandbox/tanstack-form-bug-7yxscz

Steps to reproduce
  1. Enter a value for field a, leave field b empty
  2. submit → Field b shows an error since it's empty
  3. clear field a
  4. submit again (notice canSubmitWhenInvalid is set to true) → only field b shows an error even though field a is also empty
Expected behavior

On submit, both stages should run unconditionally and isValid should be evaluated once over the combined result:

await this.validateAllFields('submit')
await this.validate('submit')

if (!this.state.isValid) {
  // onSubmitInvalid with the full error picture
  return
}
// onSubmit

Why I think this is the right behavior:

  • It matches user intuition for submit. Submit is the moment users most need a complete error report. Partial reports force them to fix-submit-fix-submit until they've discovered every problem, which is worse UX than showing everything at once.
  • When canSubmitWhenInvalid is false (the default), existing field errors already prevent _handleSubmit from running, so this change should have no effect unless there are filed level validators in which case all errors would now surface on submit instead of only the field-level ones, which I'd argue is also the right behavior. It changes behavior for forms that explicitly opt into canSubmitWhenInvalid: true — where a complete error report on every submit is exactly what you'd expect.

Happy to open a PR if there's a decision on this.

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

not relevant

TanStack Form adapter

react-form

TanStack Form version

1.28.0

TypeScript version

No response

Additional context

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at _handleSubmit in the React Form adapter and reproduce the behavior with the linked CodeSandbox. Trace validateAllFields('submit'), the isFieldsValid gate, and validate('submit'); done means submit validation reports errors from both stages and the invalid-submit path receives the combined result.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.