{"record":{"id":"7833e464ae7ae75b","repo":"Automattic/harper","slug":"htmlelement-not-added","errorCode":null,"errorMessage":"HTMLElement not added.","messagePattern":"HTMLElement not added\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/lint-framework/src/lint/LintFramework.ts","lineNumber":275,"sourceCode":"\t\t\t{ capture: true },\n\t\t);\n\t}\n\n\tpublic async addTarget(target: Node) {\n\t\tif (!this.targets.has(target)) {\n\t\t\tthis.targets.add(target);\n\t\t\tthis.update();\n\t\t\tthis.attachTargetListeners(target);\n\t\t}\n\t}\n\n\tpublic async removeTarget(target: HTMLElement) {\n\t\tif (this.targets.has(target)) {\n\t\t\tthis.targets.delete(target);\n\t\t\tthis.update();\n\t\t\tthis.detachTargetListeners(target);\n\t\t} else {\n\t\t\tthrow new Error('HTMLElement not added.');\n\t\t}\n\t}\n\n\t/** Return the last known ignorable lint boxes rendered on-screen. */\n\tpublic getLastIgnorableLintBoxes(): IgnorableLintBox[] {\n\t\treturn this.lastLintBoxes;\n\t}\n\n\tprivate attachTargetListeners(target: Node) {\n\t\tfor (const event of INPUT_EVENTS) {\n\t\t\ttarget.addEventListener(event, this.updateEventCallback);\n\t\t}\n\n\t\tconst observer = new MutationObserver(this.updateEventCallback);\n\t\tconst config = { subtree: true, characterData: true };\n\n\t\tif ((target as any).tagName == undefined) {\n\t\t\tobserver.observe((target as any).parentElement!, config);","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/Automattic/harper/blob/5fe7d5ab76492d83f3ecdbc3f1da83c75dcb6f83/packages/lint-framework/src/lint/LintFramework.ts#L257-L293","documentation":"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.","triggerScenarios":"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).","commonSituations":"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).","solutions":["Track which elements you added and only call removeTarget on those.","Guard removal with a membership check or a removed flag in your cleanup code.","If a re-render replaces the element, remove the old element before the DOM swap and re-add the new one."],"exampleFix":"// before\nfunction cleanup() {\n  framework.removeTarget(editorEl);\n}\n// after\nlet targetAdded = false;\nfunction cleanup() {\n  if (targetAdded) {\n    framework.removeTarget(editorEl);\n    targetAdded = false;\n  }\n}","handlingStrategy":"try-catch","validationCode":"if (framework instanceof LintFramework && element instanceof HTMLElement) {\n  // only remove elements you previously added; track them in a Set\n}","typeGuard":"function canRemove(fw: LintFramework, el: HTMLElement): boolean {\n  return addedTargets.has(el);\n}","tryCatchPattern":"try {\n  framework.removeTarget(el);\n} catch (e) {\n  if (e instanceof Error && e.message === 'HTMLElement not added.') {\n    // already removed or never added; safe to ignore in teardown\n  } else {\n    throw e;\n  }\n}","preventionTips":["Keep a Set of elements you passed to addTarget and remove only from it.","Make cleanup idempotent with a flag or WeakSet so double-teardown is a no-op.","Re-add targets after any framework re-initialization."],"tags":["javascript","dom","state-management"],"backgroundTag":"invalid-argument","analyzedSha":"5fe7d5ab76492d83f3ecdbc3f1da83c75dcb6f83","analyzedAt":"2026-09-06T11:34:38.610Z","contentChangedAt":"2026-09-06T11:34:38.610Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}