dotnet/aspnetcore · error · Error
Invalid payload for Completion message.
Error message
Invalid payload for Completion message.
What it means
A Completion message (type 3) is the terminal result of an invocation and the protocol forbids it from carrying both `result` and `error` at once. The invariant is: zero or one of them, never both, mirroring the IHubProtocol.ts CompletionMessage contract.
Source
Thrown at src/SignalR/clients/ts/signalr/src/JsonHubProtocol.ts:111
private _isInvocationMessage(message: InvocationMessage): void {
this._assertNotEmptyString(message.target, "Invalid payload for Invocation message.");
if (message.invocationId !== undefined) {
this._assertNotEmptyString(message.invocationId, "Invalid payload for Invocation message.");
}
}
private _isStreamItemMessage(message: StreamItemMessage): void {
this._assertNotEmptyString(message.invocationId, "Invalid payload for StreamItem message.");
if (message.item === undefined) {
throw new Error("Invalid payload for StreamItem message.");
}
}
private _isCompletionMessage(message: CompletionMessage): void {
if (message.result && message.error) {
throw new Error("Invalid payload for Completion message.");
}
if (!message.result && message.error) {
this._assertNotEmptyString(message.error, "Invalid payload for Completion message.");
}
this._assertNotEmptyString(message.invocationId, "Invalid payload for Completion message.");
}
private _isAckMessage(message: AckMessage): void {
if (typeof message.sequenceId !== 'number') {
throw new Error("Invalid SequenceId for Ack message.");
}
}
private _isSequenceMessage(message: SequenceMessage): void {
if (typeof message.sequenceId !== 'number') {
throw new Error("Invalid SequenceId for Sequence message.");View on GitHub (pinned to 294cab2f9b)
Solutions
- Fix the server to set either result or error on completion, never both.
- Capture the raw wire message with logMessageContent:true to confirm both fields are present.
- Upgrade the server SignalR package to a version where the completion invariant is enforced server-side.
- If testing a custom protocol, validate the message shape before sending.
Example fix
// before
{ "type": 3, "invocationId": "x", "result": 42, "error": "oops" }
// after
{ "type": 3, "invocationId": "x", "result": 42 } Defensive patterns
Strategy: validation
Validate before calling
// when constructing/sending a completion
function buildCompletion(invocationId: string, result?: any, error?: string) {
if (result !== undefined && error) {
throw new Error('result and error are mutually exclusive');
}
return { type: 3, invocationId, result, error };
} Type guard
function isValidCompletion(m: any): boolean {
return !(m?.result && m?.error);
} Prevention
- Treat result and error as mutually exclusive in any completion builder.
- Upgrade the server SignalR package so it enforces the invariant server-side.
- In tests, assert completions carry at most one of result/error.
When it happens
Trigger: Server sends {"type":3,"invocationId":"x","result":...,"error":"boom"}. This is a server-side bug or a custom hub/protocol implementation that sets both fields.
Common situations: A custom server hub that populates both fields, an older server with a serialization bug, or a buggy intermediate that injects an error string while preserving a result.
Related errors
- Invalid input for JSON hub protocol. Expected a string.
- Invalid payload.
- Invalid payload for StreamItem message.
- Invalid payload for Invocation message.
- Expected either 'error' or 'result' to be provided, but not
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/a432ec2fb22f57db.
Report an issue: GitHub.