dotnet/aspnetcore · error · Error

No interop methods are registered for renderer

Error message

No interop methods are registered for renderer ${rendererId}

What it means

Thrown by the internal getInteropMethods helper when interopMethodsByRenderer has no entry for rendererId. This helper backs updateRootComponents and event dispatch; calling either for an unattached renderer means there is no .NET bridge to invoke. It indicates the renderer is not yet (or no longer) attached at the time of the call.

Solutions

  1. Await the renderer's attached promise (getRendererAttachedPromise(rendererId)) before invoking operations.
  2. Ensure attachWebRendererInterop succeeds for the rendererId before dispatching events or updating roots.
  3. Re-attach after a circuit reconnect before issuing further interop calls.
  4. Verify the rendererId matches the one returned at creation.

Example fix

// before (throws)
updateRootComponents(rendererId, opsJson);

// after
await getRendererAttachedPromise(rendererId);
updateRootComponents(rendererId, opsJson);
Defensive patterns

Strategy: validation

Validate before calling

const attached = getRendererAttachedPromise(rendererId);
if (attached) await attached;
else throw new Error(`Renderer ${rendererId} is not attached.`);
updateRootComponents(rendererId, opsJson);

Type guard

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

Try / catch

try {
  updateRootComponents(rendererId, opsJson);
} catch (e) {
  if (/No interop methods are registered/.test(String(e?.message))) {
    await getRendererAttachedPromise(rendererId);
    updateRootComponents(rendererId, opsJson);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling updateRootComponents or dispatching an event for a rendererId that hasn't had attachWebRendererInterop called yet; calling after detach; using a rendererId that doesn't exist.

Common situations: Race where an event or root-component update fires before the renderer attaches; a reconnect scenario where the old id is reused after detach; a typo in the rendererId.

Related errors


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

Appendix: 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 3600ca084e)