dotnet/aspnetcore · error

No interop methods are registered for renderer ${rendererId}

Error message

No interop methods are registered for renderer ${rendererId}

What it means

Thrown by the private getInteropMethods helper (WebRendererInteropMethods.ts:78) when the requested rendererId has no entry in interopMethodsByRenderer. It is reached from dispatchEvent, updateRootComponents, and attachWebRendererInterop's JS-root-component branch. It signals that a renderer was asked to do work (dispatch an event, update roots) before attachWebRendererInterop completed, or after it was detached.

Source

Thrown at src/Components/Web.JS/src/Rendering/WebRendererInteropMethods.ts:81

  }
}

export function dispatchEvent(browserRendererId: number, eventDescriptor: EventDescriptor, eventArgs: any): void {
  return dispatchEventMiddleware(browserRendererId, eventDescriptor.eventHandlerId, () => {
    const interopMethods = getInteropMethods(browserRendererId);
    return interopMethods.invokeMethodAsync('DispatchEventAsync', eventDescriptor, eventArgs);
  });
}

export function updateRootComponents(browserRendererId: number, operationsJson: string): Promise<void> {
  const interopMethods = getInteropMethods(browserRendererId);
  return interopMethods.invokeMethodAsync('UpdateRootComponents', operationsJson);
}

function getInteropMethods(rendererId: number): DotNet.DotNetObject {
  const interopMethods = interopMethodsByRenderer.get(rendererId);
  if (!interopMethods) {
    throw new Error(`No interop methods are registered for renderer ${rendererId}`);
  }

  return interopMethods;
}

// On some hosting platforms, we may need to defer the event dispatch, so they can register this middleware to do so
type DispatchEventMiddlware = (browserRendererId: number, eventHandlerId: number, continuation: () => void) => void;

let dispatchEventMiddleware: DispatchEventMiddlware = (browserRendererId, eventHandlerId, continuation) => continuation();
export function addDispatchEventMiddleware(middleware: DispatchEventMiddlware): void {
  const next = dispatchEventMiddleware;
  dispatchEventMiddleware = (browserRendererId, eventHandlerId, continuation) => {
    middleware(browserRendererId, eventHandlerId, () => next(browserRendererId, eventHandlerId, continuation));
  };
}

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Await the renderer-attached promise via getRendererAttachedPromise(rendererId) before issuing events or root updates.
  2. Delay interop/event dispatching until after OnAfterRenderAsync(firstRender: true) on the .NET side, which guarantees the renderer is attached.
  3. Ensure Blazor.start() (or auto-start) completes before your components try to interact.
  4. If the renderer was disposed, suppress or cancel pending event handlers instead of dispatching them.

Example fix

// before
interopMethods.invokeMethodAsync('DispatchEventAsync', eventDescriptor, eventArgs);

// after
const attached = getRendererAttachedPromise(rendererId);
if (attached) { await attached; }
if (!isRendererAttached(rendererId)) { return; }
dispatchEvent(rendererId, eventDescriptor, eventArgs);
Defensive patterns

Strategy: validation

Validate before calling

import { isRendererAttached, getRendererAttachedPromise } from './WebRendererInteropMethods';

async function ensureRendererThenDispatch(rendererId: number, evt: EventDescriptor, args: any) {
  await getRendererAttachedPromise(rendererId);
  if (!isRendererAttached(rendererId)) return;
  dispatchEvent(rendererId, evt, args);
}

Type guard

function isReadyForInterop(rendererId: number): boolean {
  return isRendererAttached(rendererId);
}

Try / catch

try {
  updateRootComponents(rendererId, ops);
} catch (e) {
  if (/No interop methods are registered/.test((e as Error).message)) {
    // renderer not yet attached; queue and retry after attach listener fires
  } else { throw e; }
}

Prevention

When it happens

Trigger: An incoming browser event fires before the renderer is attached; updateRootComponents is invoked during prerender before the SignalR/WebAssembly circuit handshake finishes; attachWebRendererInterop tries to enableJSRootComponents while its own getInteropMethods(rendererId) lookup races; events dispatched against a detached renderer.

Common situations: Race between DOM event delegation and circuit startup on slow connections; calling .NET interop from OnInitializedAsync before the JS-side renderer is registered; hot-reload or HMR leaving stale event handlers bound to a disposed renderer; component library that dispatches events on mount before Blazor.start finishes.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/50590c99436ee755. Report an issue: GitHub.