dotnet/aspnetcore · error · Error
There is no pending async call with ID
Error message
There is no pending async call with ID ${asyncCallId}. What it means
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.
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.
Example fix
// before (manual misuse)
dispatcher.endInvokeDotNetFromJS('999', true, '{}'); // id 999 never started
// after
// only the runtime should call endInvokeDotNetFromJS; use the public
// invokeMethodAsync promise to receive results. Defensive patterns
Strategy: try-catch
Try / catch
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; } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Byte array index ' ' does not exist.
- For instance method calls, assemblyName should be null…
- Invalid JS call result type
- No call dispatcher has been set.
- The current dispatcher does not support synchronous calls…
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/605ba3e6ce50e61a.
Report an issue: GitHub.
Appendix: source
Thrown at src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts:550
// it's not something the developer gets to control, and it would be an error if it doesn't.
let result: Promise<ReadableStream>;
if (this._pendingDotNetToJSStreams.has(streamId)) {
// We've already started receiving the stream, so no longer need to track it as pending
result = this._pendingDotNetToJSStreams.get(streamId)!.streamPromise!;
this._pendingDotNetToJSStreams.delete(streamId);
} else {
// We haven't started receiving it yet, so add an entry to track it as pending
const pendingStream = new PendingStream();
this._pendingDotNetToJSStreams.set(streamId, pendingStream);
result = pendingStream.streamPromise;
}
return result;
}
private completePendingCall(asyncCallId: number, success: boolean, resultOrError: any) {
if (!this._pendingAsyncCalls.hasOwnProperty(asyncCallId)) {
throw new Error(`There is no pending async call with ID ${asyncCallId}.`);
}
const asyncCall = this._pendingAsyncCalls[asyncCallId];
delete this._pendingAsyncCalls[asyncCallId];
if (success) {
asyncCall.resolve(resultOrError);
} else {
asyncCall.reject(resultOrError);
}
}
}
function formatError(error: Error | string): string {
if (error instanceof Error) {
return `${error.message}\n${error.stack}`;
}
return error ? error.toString() : "null";View on GitHub (pinned to 3600ca084e)