{"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":"Error","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/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts#L532-L568","documentation":"Thrown by CallDispatcher.completePendingCall() when .NET reports completion for an asyncCallId that is not in the _pendingAsyncCalls map. Each async JS->.NET call registers an ID at dispatch time; receiving a completion for an unknown ID means the call was never started, already completed, or the ID is corrupt.","triggerScenarios":"The .NET side calls endInvokeDotNetFromJS with a callId that doesn't match any beginInvokeDotNetFromJS call; double-completion (the same call ID resolved twice); the call entry was already deleted by a prior completion. Typically indicates a runtime/protocol mismatch.","commonSituations":"Version skew between the JS interop library and the .NET runtime (ID format changed); a hot-reload or signal-reconnect replaying old completions; manual misuse of the internal dispatcher API.","solutions":["Ensure the Microsoft.JSInterop.JS npm package / static asset version matches the .NET runtime version.","Avoid calling the internal endInvokeDotNetFromJS / completePendingCall APIs directly; let Blazor manage them.","After a Blazor reconnect, re-issue interop calls rather than relying on stale IDs."],"exampleFix":"// before (manual misuse)\ndispatcher.endInvokeDotNetFromJS('999', true, '{}'); // id 999 never started\n\n// after\n// only the runtime should call endInvokeDotNetFromJS; use the public\n// invokeMethodAsync promise to receive results.","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { return await dispatcher.invokeDotNetStaticMethodAsync('MyLib', 'Method'); } catch (e) { if (/no pending async call/.test(e.message)) { console.error('Stale or unknown async call id — possible version skew or reconnect replay.'); } throw e; }","preventionTips":["Keep the JS interop asset version in lockstep with the .NET runtime.","Never call the internal endInvokeDotNetFromJS / completePendingCall APIs manually.","After a Blazor reconnect, re-issue pending interop calls instead of relying on old IDs."],"tags":["js-interop","blazor","async","dispatcher","protocol"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}