alibaba/page-agent · error · Error

Element is not an input, textarea, or contenteditable

Error message

Element is not an input, textarea, or contenteditable

What it means

inputTextElement only accepts <input>, <textarea>, or contenteditable elements; anything else (div, button, span) throws this error. Text entry requires a value-bearing or editable surface, so the library validates the element kind before typing. Receiving a non-editable element usually means the wrong index was chosen or the element role changed.

Source

Thrown at packages/page-controller/src/actions.ts:134

	// Release
	target.dispatchEvent(new PointerEvent('pointerup', pointerOpts))
	target.dispatchEvent(new MouseEvent('mouseup', mouseOpts))

	// Click — activation behavior (navigation, form submit, etc.) triggers
	// via bubbling from target up to the interactive ancestor.
	target.click()

	await waitFor(0.2)
}

/**
 * @private Internal method, subject to change at any time.
 */
export async function inputTextElement(element: HTMLElement, text: string) {
	const isContentEditable = element.isContentEditable
	if (!isInputElement(element) && !isTextAreaElement(element) && !isContentEditable) {
		throw new Error('Element is not an input, textarea, or contenteditable')
	}

	await clickElement(element)

	if (isContentEditable) {
		// Contenteditable support (partial)
		// Not supported:
		// - Monaco/CodeMirror: Require direct JS instance access. No universal way to obtain.
		// - Draft.js: Not responsive to synthetic/execCommand/Range/DataTransfer. Unmaintained.
		//
		// Strategy: Try Plan A (synthetic events) first, then verify and fall back
		// to Plan B (execCommand) if the text wasn't actually inserted.
		//
		// Plan A: Dispatch synthetic events
		// Works: React contenteditable, Quill.
		// Fails: Slate.js, some contenteditable editors that ignore synthetic events.
		// Sequence: beforeinput -> mutation -> input -> change -> blur

View on GitHub (pinned to d02db1ee7c)

Solutions

  1. Inspect the simplified HTML and target the index of the actual <input>/<textarea> or a contenteditable node
  2. For wrapper components, click the wrapper first to focus, then find the revealed input and type into it
  3. Guard before typing: check element.tagName or isContentEditable
  4. If the site uses a fake-input div, consider dispatching keyboard events instead of inputText

Example fix

// before
await controller.inputText(12, 'query') // index 12 is a <div>

// after
await controller.updateTree()
// find the real input element index (e.g. 13) in getSimplifiedHTML()
await controller.inputText(13, 'query')
Defensive patterns

Strategy: type-guard

Validate before calling

const el = await controller.element(i); if (!el) return; if (!(el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) && !el.isContentEditable) { /* click to reveal the real input, re-index, skip inputText */ }

Type guard

function isTextEntryTarget(el: HTMLElement): boolean { return el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el.isContentEditable }

Try / catch

try { await controller.inputText(i, text) } catch (e) { if (String(e).includes('not an input, textarea, or contenteditable')) { await controller.clickElement(i); await controller.updateTree(); /* find revealed input */ } else throw e }

Prevention

When it happens

Trigger: Calling inputText on an index pointing to a <div>, <button>, <label>, or any non-editable element; the input was replaced by a rich-text div that is not contenteditable; acting on a container/wrapper instead of the inner <input> (common with custom dropdowns and search boxes).

Common situations: LLM picks the visible label/wrapper instead of the inner input; custom components render a fake input div without contenteditable; the field switched to readonly/disabled custom widget between observation and action.

Related errors


AI-assisted analysis of alibaba/page-agent@d02db1ee7c (2026-08-28). Data as JSON: /api/errors/172263e472cdb5c6. Report an issue: GitHub.