{"record":{"id":"605ba3e6ce50e61a","repo":"dotnet/aspnetcore","slug":"there-is-no-pending-async-call-with-id-asynccall","errorCode":null,"errorMessage":"There is no pending async call with ID ${asyncCallId}.","messagePattern":"There is no pending async call with ID (.+?)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts","lineNumber":550,"sourceCode":"          // it's not something the developer gets to control, and it would be an error if it doesn't.\n          let result: Promise<ReadableStream>;\n          if (this._pendingDotNetToJSStreams.has(streamId)) {\n              // We've already started receiving the stream, so no longer need to track it as pending\n              result = this._pendingDotNetToJSStreams.get(streamId)!.streamPromise!;\n              this._pendingDotNetToJSStreams.delete(streamId);\n          } else {\n              // We haven't started receiving it yet, so add an entry to track it as pending\n              const pendingStream = new PendingStream();\n              this._pendingDotNetToJSStreams.set(streamId, pendingStream);\n              result = pendingStream.streamPromise;\n          }\n\n          return result;\n      }\n\n      private completePendingCall(asyncCallId: number, success: boolean, resultOrError: any) {\n          if (!this._pendingAsyncCalls.hasOwnProperty(asyncCallId)) {\n              throw new Error(`There is no pending async call with ID ${asyncCallId}.`);\n          }\n\n          const asyncCall = this._pendingAsyncCalls[asyncCallId];\n          delete this._pendingAsyncCalls[asyncCallId];\n          if (success) {\n              asyncCall.resolve(resultOrError);\n          } else {\n              asyncCall.reject(resultOrError);\n          }\n      }\n  }\n\n  function formatError(error: Error | string): string {\n      if (error instanceof Error) {\n          return `${error.message}\\n${error.stack}`;\n      }\n\n      return error ? error.toString() : \"null\";","sourceCodeStart":532,"sourceCodeEnd":568,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts#L532-L568","documentation":"Thrown by completePendingCall when _pendingAsyncCalls has no entry for the given asyncCallId. Each async JS<->.NET call registers a deferred resolver/rejector keyed by an incrementing id; completion removes it. The id is unknown if it was already completed (double completion), cleared on disconnect, or generated by a stale/out-of-process message.","triggerScenarios":"The .NET side calling back to complete a call twice; a reconnect/hot-reload where the JS dispatcher's _nextAsyncCallId counter reset but a queued completion arrives; manually calling DotNet plumbing such as __blazor__ completeAsyncCall more than once. In normal framework usage this indicates a framework-level or transport-level bug rather than app code.","commonSituations":"Blazor Server reconnect scenarios, dev hot reload, aggressive disposal while async calls are in flight, or a misbehaving custom transport that replays completion frames.","solutions":["Confirm you are on the latest Blazor runtime version; double-completion is usually a framework bug fixed in patch releases.","Ensure the SignalR connection is not reconnecting mid-call; handle OnConnectionDown and re-issue the call instead of relying on stale callbacks.","If calling framework internals, verify each asyncCallId is completed exactly once.","Guard app-level code with try/catch around the async call so a spurious completion rejection is observable."],"exampleFix":"// before\n// relying on a single fire-and-forget that may be completed twice\nawait dotNetRef.invokeMethodAsync('Op');\n\n// after\ntry {\n  await dotNetRef.invokeMethodAsync('Op');\n} catch (e) {\n  // a duplicate/stale completion surfaces here\n  console.warn('dotnet call failed/stale:', e.message);\n}","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  await dotNetRef.invokeMethodAsync('Op');\n} catch (e) {\n  if (/no pending async call with ID/i.test(e.message)) {\n    // stale/duplicate completion; re-issue once on a fresh ref\n    return await dotNetRef.invokeMethodAsync('Op');\n  }\n  throw e;\n}","preventionTips":["Treat this error as a symptom of double-completion or stale connection state.","Avoid holding call ids across reconnects; re-issue after reconnect.","Keep Blazor runtime updated to pick up completion fixes."],"tags":["blazor","jsinterop","async","internal","reconnect"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}