FormGroupApi.getRelatedFields and FieldApi.validate() use unguarded startsWith for group membership, matching unrelated fields with a shared name prefix
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 82/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript
- Domain
- frontend
Research direction
Start with isFieldInGroup in packages/form-core/src/utils.ts, then inspect getRelatedFields() in packages/form-core/src/FormGroupApi.ts and validate() in packages/form-core/src/FieldApi.ts. Run the relevant form-core tests and add coverage for a group named user alongside an unrelated username field; done means group operations and validation no longer include the unrelated field.
Written by the indexing model from the issue text.
Description
I found another instance of the string-prefix bug that #2318 just fixed for deleteField.
FormApi.deleteField used to select sub-fields with a raw f.startsWith(fieldStr), which meant deleting field email also deleted the unrelated sibling emailVerified (#2317). That was fixed in #2318 by requiring a . or [ boundary after the prefix, and packages/form-core/src/utils.ts now has a proper isFieldInGroup(groupName, fieldName) helper that does the same boundary check for FormGroups:
export function isFieldInGroup(groupName: string, fieldName: string) {
return (
fieldName === groupName ||
fieldName.startsWith(`${groupName}.`) ||
fieldName.startsWith(`${groupName}[`)
)
}
I checked where FormGroups associate fields with themselves and found two places that don't use this helper and still do the raw unguarded startsWith:
FormGroupApi.getRelatedFields()(packages/form-core/src/FormGroupApi.ts, around line 1649):
if (field.instance.name.startsWith(this.name)) {
relatedFields.push(field.instance)
}
FieldApi.validate()(packages/form-core/src/FieldApi.ts, around line 1628), when computing which groups "encompass" a field:
const encompassingGroups = opts?.skipGroupValidation
? []
: Array.from(this.form.formGroupApis).filter((group) =>
this.name.startsWith(group.name),
)
I confirmed getRelatedFields() isn't just informational, it's used by validate(), validateAllFields(), areRelatedFieldsValid(), and handleSubmit() on FormGroupApi to decide which fields belong to the group, get touched on submit, and get counted toward the group's validity.
Repro: a form with a FormGroup named user and an unrelated top-level field named username. Submitting the user group marks username as touched and includes its validity in areRelatedFieldsValid(), even though username isn't nested under the group at all. Similarly, changing username makes FieldApi.validate() treat the user group as "encompassing" it and rerun that group's form-level validators on every change to username.
I compared this against isFieldInGroup in utils.ts, which already has the correct fix, and against getRelatedFieldMetasDerived() in the same FormGroupApi.ts file, which does use isFieldInGroup correctly (lines 1671, 2314, 2334). So the fix pattern already exists in the codebase, it's just not applied consistently at the two call sites above.
Suggested fix: swap both raw startsWith checks for isFieldInGroup(this.name, field.instance.name) in getRelatedFields() and isFieldInGroup(group.name, this.name) in FieldApi.validate().
- Dominant language
- TypeScript
- Stars
- 6.7k
- Forks
- 682
- Avg merge
- 5d 18h
- Merged PRs (30d)
- 7
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from TanStack/form
-
Difficulty 2/5 1-2 days Newbie friendliness 72/100
-
area: runtime scope: devtools type: bug v2
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
-
FormApi: onSubmit/onServer error cleared on any non-matching validation cause, not just 'change' Openv1 v2: needs investigation
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
v1 v2: needs investigation
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
-
v1 v2: needs investigation
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Eynzof/Hermes-CN-Desktop#610 ·
-
bug clawsweeper:linked-pr-open clawsweeper:needs-live-repro clawsweeper:no-new-fix-pr impact:message-loss issue-rating: 🐚 platinum hermit P2 regression
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
danielmiessler/LifeOS#2218 ·