puppeteer/puppeteer · error · Error
Element cannot be filled out.
Error message
Element cannot be filled out.
What it means
Thrown by Locator.fill() when the target element is classified as an 'unknown' input type that Puppeteer cannot fill. The locator first categorizes the element (checkable-input, select, contenteditable, typeable-input, other-input, unknown); only 'unknown' throws, meaning the element is neither a recognizable form control nor editable content.
Source
Thrown at packages/puppeteer-core/src/api/locators/locators.ts:611
(input as HTMLInputElement).value = '';
(input as HTMLInputElement).value = currentValue;
}
return valString.substring(currentValue.length);
}, value),
).pipe(
mergeMap(textToType => {
if (!textToType) {
return of(undefined);
}
return from(handle.type(textToType));
}),
);
}
return fillDirectly();
case 'other-input':
return fillDirectly();
case 'unknown':
throw new Error(`Element cannot be filled out.`);
}
}),
)
.pipe(
catchError(err => {
void handle.dispose().catch(error => {
this.#logger?.(DEBUG_PREFIXES.error)?.(error);
});
throw err;
}),
);
}),
this.operators.retryAndRaceWithSignalAndTimer(signal, cause),
);
}
#hover<ElementType extends Element>(
this: Locator<ElementType>,View on GitHub (pinned to d484e21c17)
Solutions
- Point the locator at a real fillable element: input, textarea, select, or a contentEditable node.
- If you must set text on a non-input, use `locator.evaluate((el, v) => { el.textContent = v; }, value)` instead.
- For custom elements, expose a value or make them contentEditable so the locator recognizes them.
Example fix
// before
await page.locator('div.greeting').fill('hello');
// after
await page.locator('input#name').fill('hello'); Defensive patterns
Strategy: validation
Validate before calling
const tag = await el.evaluate(node => node.tagName.toLowerCase());
if (!['input', 'textarea', 'select'].includes(tag) &&
!(await el.evaluate(n => (n as HTMLElement).isContentEditable))) {
throw new Error('element is not fillable');
}
await page.locator(sel).fill('text'); Type guard
async function isFillable(el: ElementHandle): Promise<boolean> {
return el.evaluate(node => {
const n = node as HTMLElement;
return ['INPUT', 'TEXTAREA', 'SELECT'].includes(n.tagName) || n.isContentEditable;
});
} Try / catch
try {
await page.locator(sel).fill('text');
} catch (e) {
if (e instanceof Error && /cannot be filled/.test(e.message)) {
await page.locator(sel).evaluate((el, v) => { (el as HTMLElement).textContent = v; }, 'text');
} else throw e;
} Prevention
- Verify the selector targets an actual input/textarea/select/contentEditable.
- Use waitForSelector before fill to ensure the element exists and is laid out.
- For custom elements, set value via evaluate instead of fill.
When it happens
Trigger: Calling `page.locator('div#label').fill('text')` on a non-input, non-editable element such as a plain div, span, or image. Also triggered on custom elements that don't expose a value or contentEditable interface.
Common situations: Selector matches a wrapper/container instead of the actual input; shadow-DOM custom element that Puppeteer can't introspect; wrong locator target after a DOM refactor.
Related errors
- Unknown locator for race candidate
- Unsupported browser '${browser.name}'
- Unsupported platform '${platform}'
- No pinned version found for ${browserName}
- ${tag.toUpperCase()} is not available for Firefox
AI-assisted analysis of puppeteer/puppeteer@d484e21c17 (2026-08-12).
Data as JSON: /api/errors/8f54721fa18a2293.
Report an issue: GitHub.