microsoft/playwright · error · NonRecoverableDOMError
Cannot uncheck radio button. Radio buttons can only be unche
Error message
Cannot uncheck radio button. Radio buttons can only be unchecked by selecting another radio button in the same group.
What it means
Radio buttons (input[type=radio]) can only be turned on, never off — unchecked by selecting a sibling radio in the same name group. _setChecked throws NonRecoverableDOMError when asked to set state=false and the element is a radio (isRadio:true), because toggling a radio to false is not a valid DOM operation.
Source
Thrown at packages/playwright-core/src/server/dom.ts:836
async uncheck(progress: Progress, options: { position?: types.Point } & types.PointerActionWaitOptions) {
const result = await this._setChecked(progress, false, options);
return assertDone(throwRetargetableDOMError(result));
}
async _setChecked(progress: Progress, state: boolean, options: { position?: types.Point } & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> {
const isChecked = async (progress: Progress) => {
const result = await progress.race(this.evaluateInUtility(([injected, node]) => injected.elementState(node, 'checked'), {}));
if (result === 'error:notconnected' || result.received === 'error:notconnected')
throwElementIsNotAttached();
return { matches: result.matches, isRadio: result.isRadio };
};
await this._markAsTargetElement(progress);
const checkedState = await isChecked(progress);
if (checkedState.matches === state)
return 'done';
if (!state && checkedState.isRadio)
throw new NonRecoverableDOMError('Cannot uncheck radio button. Radio buttons can only be unchecked by selecting another radio button in the same group.');
const result = await this._click(progress, { ...options, waitAfter: 'disabled' });
if (result !== 'done')
return result;
if (options.trial)
return 'done';
const finalState = await isChecked(progress);
if (finalState.matches !== state)
throw new NonRecoverableDOMError('Clicking the checkbox did not change its state');
return 'done';
}
async boundingBox(progress: Progress): Promise<types.Rect | null> {
return await progress.race(this._page.delegate.getBoundingBox(this));
}
async screenshot(progress: Progress, options: ScreenshotOptions): Promise<Buffer> {
return await this._page.screenshotter.screenshotElement(progress, this, options);
}View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Do not uncheck radios; to deselect, check a different radio in the same name group (or a dedicated 'none' option).
- Detect the input type before calling uncheck and skip radios.
- Reset the radio group by selecting another option rather than unchecking the current one.
Example fix
// before
await page.locator('input[name=opt]').uncheck(); // radio → throws
// after — select a different radio to change selection
await page.locator('input[name=opt][value=none]').check(); Defensive patterns
Strategy: type-guard
Validate before calling
// Skip radios when unchecking a group of toggles.
const type = await locator.getAttribute('type');
if (type !== 'radio') await locator.uncheck(); Type guard
// Treat radios as non-uncheckable.
async function isRadio(locator: Locator): Promise<boolean> {
return (await locator.getAttribute('type')) === 'radio';
} Prevention
- Never call uncheck() on input[type=radio].
- Detect the input type before generic uncheck loops.
- To change a radio selection, check a different option in the group.
When it happens
Trigger: Calling locator.uncheck() (or setChecked(false)) on an input[type=radio]; generic form-reset code that unchecks every checkbox and radio in a group.
Common situations: Looping over all toggleable inputs and calling uncheck on each; treating radios like checkboxes in shared helper code; trying to clear a form by unchecking radios.
Related errors
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/cecbf3a1aa1f4fc6.
Report an issue: GitHub.