Automattic/harper · error · Error
TextFieldRange expects an HTMLTextAreaElement or HTMLInputEl
Error message
TextFieldRange expects an HTMLTextAreaElement or HTMLInputElement
What it means
The TextFieldRange constructor validates its first argument with instanceof, requiring an HTMLTextAreaElement or HTMLInputElement. Any other element (div, contenteditable span, etc.) cannot back a text-field range, so the constructor fails fast instead of misbehaving later.
Source
Thrown at packages/lint-framework/src/lint/TextFieldRange.ts:35
}
> = new WeakMap();
private arena: { mirror: HTMLDivElement; text: Text; refs: number };
/**
* Create a range-like object for a given text input field.
* @param field - A HTMLTextAreaElement or a HTMLInputElement (of type "text").
* @param startOffset - The starting character index.
* @param endOffset - The ending character index.
*/
constructor(
field: HTMLTextAreaElement | HTMLInputElement,
startOffset: number,
endOffset: number,
) {
// In this case we assume the caller provided a text field
if (!(field instanceof HTMLTextAreaElement || field instanceof HTMLInputElement)) {
throw new Error('TextFieldRange expects an HTMLTextAreaElement or HTMLInputElement');
}
this.field = field;
this.startOffset = startOffset;
this.endOffset = endOffset;
this.arena = TextFieldRange.ensureArena(this.field);
this.arena.refs++;
}
/**
* Creates (or reuses) an off-screen mirror element that mimics the field's styles
* and positions it exactly over the field.
*/
private static ensureArena(field: HTMLTextAreaElement | HTMLInputElement): {
mirror: HTMLDivElement;
text: Text;
refs: number;
} {
const existing = TextFieldRange.arenas.get(field);View on GitHub (pinned to 5fe7d5ab76)
Solutions
- Pass the actual <textarea> or <input> element to the constructor.
- If you have a contenteditable surface, use the framework's non-text-field API (addTarget with an HTMLElement) instead of TextFieldRange.
- Narrow your element selection (querySelector('textarea, input')) before constructing.
Example fix
// before
const range = new TextFieldRange(document.querySelector('.editor'), 0, 10);
// after
const el = document.querySelector('textarea.editor, input.editor');
if (!el) throw new Error('No text field found');
const range = new TextFieldRange(el, 0, 10); Defensive patterns
Strategy: type-guard
Validate before calling
if (!(el instanceof HTMLTextAreaElement || el instanceof HTMLInputElement)) {
throw new TypeError('TextFieldRange requires a textarea or input element');
} Type guard
function isTextField(el: Element): el is HTMLTextAreaElement | HTMLInputElement {
return el instanceof HTMLTextAreaElement || el instanceof HTMLInputElement;
} Try / catch
try {
const range = new TextFieldRange(field, start, end);
} catch (e) {
console.error('TextFieldRange requires a textarea/input; got', field);
}
Prevention
- Type the parameter as HTMLTextAreaElement | HTMLInputElement so TypeScript rejects other elements at compile time.
- Narrow with querySelector('textarea, input') before constructing.
- Use addTarget-based APIs for contenteditable or non-input surfaces.
When it happens
Trigger: new TextFieldRange(el, start, end) where el is not a <textarea> or <input>; common with contenteditable divs, generic HTMLElements, or elements fetched by a broad selector.
Common situations: Attaching the linter to a rich-text/contenteditable editor instead of a plain text field, passing a wrapper div, or running in a non-DOM environment (SSR) where instanceof checks fail or the element is a stub.
Related errors
- HTMLElement not added.
- Unexpected lint kind: ${lintKindKey}
- Unhandled case: type undefined
- Expected a DOM Element
- fileDict path must be a string.
AI-assisted analysis of Automattic/harper@5fe7d5ab76 (2026-09-06).
Data as JSON: /api/errors/8ac82ece220057cc.
Report an issue: GitHub.