dotnet/aspnetcore · error · Error

Unable to focus an invalid element.

Error message

Unable to focus an invalid element.

What it means

Blazor's interop `focus` helper moves keyboard focus to a DOM element passed from .NET (e.g. via `FocusAsync`/`@ref`). It branches on `instanceof HTMLElement` then `instanceof SVGElement`; if the value matches neither branch it is treated as invalid and the call throws. In practice the element is null/undefined, a non-element node, or a detached node whose identity no longer satisfies the instanceof checks.

Solutions

  1. Null-check the element reference on the .NET side (`@if (myRef is not null) await myRef.FocusAsync();`) before invoking focus.
  2. Ensure the focused element is a real, attached HTMLElement/SVGElement — verify `.isConnected` or that the owning `@if`/`@foreach` branch is still rendered at call time.
  3. Prefer `focusBySelector` (CSS-selector overload) when you only have a selector: it silently no-ops when `querySelector` finds nothing instead of throwing.
  4. For SVG targets, add `tabindex="-1"` so the SVG branch succeeds rather than hitting the invalid/missing-tabindex errors.

Example fix

// before
const el = document.querySelector('.maybe-gone');
focus(el, false); // el is null -> 'Unable to focus an invalid element.'

// after
const el = document.querySelector('.maybe-gone');
if (el instanceof HTMLElement || el instanceof SVGElement) {
  focus(el, false);
}
Defensive patterns

Strategy: type-guard

Validate before calling

const el = document.querySelector(sel);
if (!el) return; // nothing matched
if (!(el instanceof HTMLElement) && !(el instanceof SVGElement)) {
  throw new Error('Selector did not resolve a focusable element.');
}
focus(el, false);

Type guard

function isFocusable(el: unknown): el is HTMLElement | SVGElement {
  return el instanceof HTMLElement || el instanceof SVGElement;
}

Try / catch

try { focus(element, preventScroll); }
catch (e) { if (/invalid element/.test(e.message)) { /* degrade silently or log */ } else throw e; }

Prevention

When it happens

Trigger: Calling `domFunctions.focus(element, preventScroll)` (directly or through `FocusAsync`) when `element` is null, undefined, a text node, a Document/Window, or an element reference held by .NET that was removed during a re-render so it is no longer an HTMLElement/SVGElement at runtime.

Common situations: Focusing an element inside an `@if` block that was toggled off before the await completed; holding a stale `ElementReference` across a hot reload or navigation; passing the result of `querySelector` that returned null; focusing a component host whose underlying DOM node was replaced by diffing.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/2a04c17080f08d77. Report an issue: GitHub.

Appendix: source

Thrown at src/Components/Web.JS/src/DomWrapper.ts:21

import '@microsoft/dotnet-js-interop';

export const domFunctions = {
  focus,
  focusBySelector,
};

function focus(element: HTMLOrSVGElement, preventScroll: boolean): void {
  if (element instanceof HTMLElement) {
    element.focus({ preventScroll });
  } else if (element instanceof SVGElement) {
    if (element.hasAttribute('tabindex')) {
      element.focus({ preventScroll });
    } else {
      throw new Error('Unable to focus an SVG element that does not have a tabindex.');
    }
  } else {
    throw new Error('Unable to focus an invalid element.');
  }
}

function focusBySelector(selector: string) {
  const element = document.querySelector(selector) as HTMLElement;
  if (element) {
    // If no explicit tabindex is defined, mark it as programmatically-focusable.
    // This does actually add a new HTML attribute, but it shouldn't interfere with
    // diffing because diffing only deals with the attributes you have in your code.
    if (!element.hasAttribute('tabindex')) {
      element.tabIndex = -1;
    }

    element.focus({ preventScroll: true });
  }
}

View on GitHub (pinned to 3600ca084e)