vitest-dev/vitest · error · Error
Multiple form elements with the same name must be of the…
Error message
Multiple form elements with the same name must be of the same type
What it means
Thrown internally by `getMultiElementValue` inside `toHaveFormValues` when a form contains two or more elements sharing the same `name` attribute but with different `type` values (e.g. a checkbox and a text input both named `choice`). The matcher groups same-named elements to compute a combined value (radio → selected value, checkboxes → array of checked values); mixing types makes that aggregation ambiguous, so it aborts rather than silently returning a wrong value.
Solutions
- Rename one of the colliding inputs so each `name` maps to a single control type.
- Split the expectation: query each element directly with a Locator and assert with `toHaveValue` / `toBeChecked` instead of aggregating via `toHaveFormValues`.
- If a list of values is intended, use a single control type (e.g. multiple `type="checkbox"` with `name="items[]"`).
- Inspect the form markup with the browser inspector and confirm every duplicated `name` has the same `type`.
Example fix
<!-- before --> <input type="checkbox" name="choice" /> <input type="text" name="choice" /> <!-- after --> <input type="checkbox" name="choice" /> <input type="checkbox" name="choice-alt" />
Defensive patterns
Strategy: validation
Validate before calling
// verify each duplicated name maps to a single input type before asserting
function assertNoMixedTypes(form: HTMLFormElement) {
const byName = new Map<string, Set<string>>()
for (const el of form.elements) {
if ('name' in el && el.name) {
const types = byName.get(el.name) ?? new Set<string>()
types.set((el as HTMLInputElement).type ?? el.tagName)
byName.set(el.name, types)
if (types.size > 1) throw new Error(`name '${el.name}' used by mixed input types`)
}
}
} Prevention
- Use distinct `name` attributes per control type.
- For value lists, use one control type (e.g. multiple checkboxes with `name[]`).
- When a name must be shared, assert with element-level matchers instead of `toHaveFormValues`.
When it happens
Trigger: A `<form>`/`<fieldset>` where two elements share `name="foo"` but one is `<input type="checkbox" name="foo">` and the other is `<input type="text" name="foo">`, then calling `expect(form).toHaveFormValues({ foo: ... })`. Also triggered by a radio group mixed with a non-radio input of the same name.
Common situations: Shared `name` collisions from copy-pasted inputs; legacy forms reusing a name for different control types; frameworks that auto-generate names; testing a form whose markup is genuinely invalid for the matcher's grouping model.
Related errors
- input with type=checkbox or type=radio cannot be used with…
- received value must a Node or a Locator that returns a Node.
- received value must an HTMLElement or an SVGElement or a…
- .toHaveDisplayValue() currently does not support input
- .toHaveDisplayValue() currently supports only input…
AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11).
Data as JSON: /api/errors/c0121e01f1a2ef91.
Report an issue: GitHub.
Appendix: source
Thrown at packages/browser/src/client/tester/expect/toHaveFormValues.ts:72
commonKeyValues[key] = formValues[key]
}
return [
this.utils.matcherHint(matcher, 'element', ''),
`Expected the element ${to} have form values`,
this.utils.diff(expectedValues, commonKeyValues),
].join('\n\n')
},
}
}
// Returns the combined value of several elements that have the same name
// e.g. radio buttons or groups of checkboxes
function getMultiElementValue(elements: HTMLInputElement[]) {
let type = ''
for (const element of elements) {
if (type && type !== element.type) {
throw new Error(
'Multiple form elements with the same name must be of the same type',
)
}
type = element.type
}
switch (type) {
case 'radio': {
const selected = elements.find(radio => radio.checked)
return selected ? selected.value : undefined
}
case 'checkbox':
return elements
.filter(checkbox => checkbox.checked)
.map(checkbox => checkbox.value)
default:
// NOTE: Not even sure this is a valid use case, but just in case...
return elements.map(element => element.value)
}View on GitHub (pinned to 1fa9837ec2)