Automattic/harper · error · Error

HTMLElement not added.

Error message

HTMLElement not added.

What it means

LintFramework.removeTarget() throws this when the HTMLElement you pass was never registered via addTarget(). The framework only removes targets it tracks in its internal `targets` map; removing an unknown element is treated as a caller bug rather than a no-op.

Source

Thrown at packages/lint-framework/src/lint/LintFramework.ts:275

			{ capture: true },
		);
	}

	public async addTarget(target: Node) {
		if (!this.targets.has(target)) {
			this.targets.add(target);
			this.update();
			this.attachTargetListeners(target);
		}
	}

	public async removeTarget(target: HTMLElement) {
		if (this.targets.has(target)) {
			this.targets.delete(target);
			this.update();
			this.detachTargetListeners(target);
		} else {
			throw new Error('HTMLElement not added.');
		}
	}

	/** Return the last known ignorable lint boxes rendered on-screen. */
	public getLastIgnorableLintBoxes(): IgnorableLintBox[] {
		return this.lastLintBoxes;
	}

	private attachTargetListeners(target: Node) {
		for (const event of INPUT_EVENTS) {
			target.addEventListener(event, this.updateEventCallback);
		}

		const observer = new MutationObserver(this.updateEventCallback);
		const config = { subtree: true, characterData: true };

		if ((target as any).tagName == undefined) {
			observer.observe((target as any).parentElement!, config);

View on GitHub (pinned to 5fe7d5ab76)

Solutions

  1. Track which elements you added and only call removeTarget on those.
  2. Guard removal with a membership check or a removed flag in your cleanup code.
  3. If a re-render replaces the element, remove the old element before the DOM swap and re-add the new one.

Example fix

// before
function cleanup() {
  framework.removeTarget(editorEl);
}
// after
let targetAdded = false;
function cleanup() {
  if (targetAdded) {
    framework.removeTarget(editorEl);
    targetAdded = false;
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (framework instanceof LintFramework && element instanceof HTMLElement) {
  // only remove elements you previously added; track them in a Set
}

Type guard

function canRemove(fw: LintFramework, el: HTMLElement): boolean {
  return addedTargets.has(el);
}

Try / catch

try {
  framework.removeTarget(el);
} catch (e) {
  if (e instanceof Error && e.message === 'HTMLElement not added.') {
    // already removed or never added; safe to ignore in teardown
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling framework.removeTarget(el) where el was never added with addTarget(el), or was already removed in a previous call (the second call sees it missing from the map).

Common situations: Double-removal on component teardown (e.g. cleanup running twice), removing an element after framework re-initialization, or passing a different DOM node than the one registered (e.g. re-rendered element replaced the original).

Understand the failure class

Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.

Related errors


AI-assisted analysis of Automattic/harper@5fe7d5ab76 (2026-09-06). Data as JSON: /api/errors/7833e464ae7ae75b. Report an issue: GitHub.