{"record":{"id":"83da029c2d66ed0f","repo":"dotnet/aspnetcore","slug":"the-circuit-associated-with-this-dispatcher-is-no","errorCode":null,"errorMessage":"The circuit associated with this dispatcher is no longer available.","messagePattern":"The circuit associated with this dispatcher is no longer available\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts","lineNumber":528,"sourceCode":"\n  // Implements DotNet.DotNetCallDispatcher\n  public endInvokeJSFromDotNet(asyncHandle: number, succeeded: boolean, argsJson: any): void {\n    this.throwIfDispatchingWhenDisposed();\n    if (asyncHandle !== 0) {\n      this.changeActivity(-1);\n    }\n    this._connection!.send('EndInvokeJSFromDotNet', asyncHandle, succeeded, argsJson);\n  }\n\n  // Implements DotNet.DotNetCallDispatcher\n  public sendByteArray(id: number, data: Uint8Array): void {\n    this.throwIfDispatchingWhenDisposed();\n    this._connection!.send('ReceiveByteArray', id, data);\n  }\n\n  private throwIfDispatchingWhenDisposed() {\n    if (this._disposed) {\n      throw new Error('The circuit associated with this dispatcher is no longer available.');\n    }\n  }\n\n  public sendLocationChanged(uri: string, state: string | undefined, intercepted: boolean): Promise<void> {\n    return this._connection!.send('OnLocationChanged', uri, state, intercepted);\n  }\n\n  public sendLocationChanging(callId: number, uri: string, state: string | undefined, intercepted: boolean): Promise<void> {\n    return this._connection!.send('OnLocationChanging', callId, uri, state, intercepted);\n  }\n\n  public sendJsDataStream(data: ArrayBufferView | Blob, streamId: number, chunkSize: number) {\n    this.changeActivity(1);\n    return sendJSDataStream(this._connection!, data, streamId, chunkSize, () => this.changeActivity(-1));\n  }\n\n  public resolveElement(sequenceOrIdentifier: string): LogicalElement {\n    // It may be a root component added by JS","sourceCodeStart":510,"sourceCodeEnd":546,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts#L510-L546","documentation":"`throwIfDispatchingWhenDisposed` guards every dispatcher method (`beginInvokeDotNetFromJS`, `endInvokeJSFromDotNet`, `sendByteArray`) so no JS->.NET calls are sent over a dead circuit. Once `dispose()`/`disposeCore()` sets `_disposed = true`, any subsequent dispatch attempt throws — the underlying SignalR connection is gone and the server-side circuit is being torn down.","triggerScenarios":"Any JS interop call routed through the dispatcher after `_disposed` becomes true: a pending `DotNet.invokeMethodAsync`, an `[JSInvokable]` callback completion, or a byte-array send that races with disposal.","commonSituations":"Long-running JS work calling back into .NET after the user navigated away or the circuit dropped; a `DotNet.invokeMethodAsync` fired from a setTimeout/interval that outlived the circuit; event handlers firing during teardown; streaming data sent after disconnect.","solutions":["Cancel/short-circuit JS-side work that may call back into .NET when the circuit is disposing (track a disposed flag in your interop wrapper).","Wrap fire-and-forget callbacks in a guard that checks dispatcher availability before invoking.","Prefer `IAsyncDisposable`/`@implements IAsyncDisposable` to tear down JS resources before the circuit is fully disposed.","Surface this as a recoverable condition in your error UI rather than an unhandled promise rejection."],"exampleFix":"// before\nsetTimeout(async () => {\n  await DotNet.invokeMethodAsync('MyAssembly', 'LateCallback'); // throws if disposed\n}, 5000);\n\n// after\nlet cancelled = false;\nexport function dispose() { cancelled = true; }\nsetTimeout(async () => {\n  if (cancelled) return;\n  try { await DotNet.invokeMethodAsync('MyAssembly', 'LateCallback'); }\n  catch { /* circuit gone - ignore */ }\n}, 5000);","handlingStrategy":"try-catch","validationCode":"let circuitDisposed = false;\nexport function markDisposed() { circuitDisposed = true; }\nasync function safeInvoke(assembly: string, method: string, ...args: unknown[]) {\n  if (circuitDisposed) return;\n  return DotNet.invokeMethodAsync(assembly, method, ...args);\n}","typeGuard":null,"tryCatchPattern":"try { await DotNet.invokeMethodAsync(asm, method); }\ncatch (e) { if (/no longer available/.test(e.message)) { /* circuit disposed - swallow */ return; } throw e; }","preventionTips":["Track a disposed flag in your JS interop wrapper and short-circuit callbacks.","Cancel JS-side timers/intervals that may call into .NET after teardown.","Implement IAsyncDisposable to tear down JS resources in order.","Treat 'circuit no longer available' as recoverable, not a hard failure."],"tags":["circuit","interop","dispose","dispatcher","blazor-server"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}