can1357/oh-my-pi · error · Error

Attribute not found: ${name}

Error message

Attribute not found: ${name}

What it means

NamedNodeMap.removeNamedItem removes and returns the attribute with the given name; when getNamedItem finds no matching Attr it throws 'Attribute not found: <name>'. Unlike the standard DOM (which throws NotFoundError with a generic message and returns null in removeAttribute), this implementation surfaces the missing attribute name explicitly because removeNamedItem's contract requires returning the removed Attr.

Source

Thrown at packages/utils/src/dom/core.ts:434

export class NamedNodeMap extends Array<Attr> {
	/** Find an attribute case-insensitively. */
	getNamedItem(name: string): Attr | null {
		const normalized = name.toLowerCase();
		return this.find(attr => attr.name.toLowerCase() === normalized) ?? null;
	}

	/** Set an attribute object and return the prior one. */
	setNamedItem(attr: Attr): Attr | null {
		const previous = this.getNamedItem(attr.name);
		if (previous) this.splice(this.indexOf(previous), 1, attr);
		else this.push(attr);
		return previous;
	}

	/** Remove and return a named attribute. */
	removeNamedItem(name: string): Attr {
		const attr = this.getNamedItem(name);
		if (!attr) throw new Error(`Attribute not found: ${name}`);
		this.splice(this.indexOf(attr), 1);
		return attr;
	}
}

/** Token-list view over an element class attribute. */
export class DOMTokenList implements Iterable<string> {
	#element: Element;

	constructor(element: Element) {
		this.#element = element;
	}

	#tokens(): string[] {
		return this.#element.className.trim() ? this.#element.className.trim().split(/\s+/) : [];
	}

	#write(tokens: string[]): void {

View on GitHub (pinned to 9690622007)

Solutions

  1. Check existence first: `if (element.hasAttribute(name)) ...` before removeNamedItem.
  2. Prefer `element.removeAttribute(name)`, which is a no-op for missing attributes instead of throwing.
  3. Verify the exact attribute name/qualification (namespace prefix, case) via element.getAttributeNames().
  4. Make removal logic idempotent by catching the error or guarding with hasAttribute when processing possibly-already-cleaned nodes.

Example fix

// before: throws when attribute is absent
element.attributes.removeNamedItem("data-id");

// after: guard, or use removeAttribute
if (element.hasAttribute("data-id")) {
  element.attributes.removeNamedItem("data-id");
}
// or simply:
element.removeAttribute("data-id"); // no-op if missing
Defensive patterns

Strategy: validation

Validate before calling

if (element.hasAttribute(name)) {
  element.attributes.removeNamedItem(name);
}

Try / catch

try {
  element.attributes.removeNamedItem(name);
} catch (err) {
  if (err instanceof Error && err.message === `Attribute not found: ${name}`) {
    // idempotent cleanup: attribute already absent — ignore
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling `element.attributes.removeNamedItem(name)` (or any API delegating to it) with an attribute name that the element does not have — typo'd name, wrong namespace-qualified form (e.g. 'xlink:href' vs 'href'), or the attribute was already removed.

Common situations: Best-effort attribute cleanup that assumes an attribute exists; case-sensitivity mismatches (attribute names are case-sensitive here); removing namespaced attributes using only the local name; scripts run twice so the second pass finds nothing to remove.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/b26b5ff30b378f2a. Report an issue: GitHub.