dotnet/aspnetcore · error · Error

Invalid payload for Completion message.

Error message

Invalid payload for Completion message.

What it means

Thrown by JsonHubProtocol._isCompletionMessage for three distinct malformed-Completion conditions: (1) both `result` and `error` are truthy, (2) `error` is truthy but `result` is falsy AND `error` is not a non-empty string, or (3) `invocationId` is not a non-empty string. A well-formed Completion carries exactly one of error/result and always a non-empty invocationId.

Solutions

  1. Ensure the server sets either `result` or `error` (never both) on Completion.
  2. Make sure error is a non-empty string when present.
  3. Always include the invocationId matching the originating invocation/stream.
Defensive patterns

Strategy: validation

Validate before calling

function validCompletion(m: any): boolean {
  const hasResult = !!m.result;
  const hasErr = !!m.error;
  if (hasResult && hasErr) return false;
  if (hasErr && !hasResult && (typeof m.error !== "string" || m.error === "")) return false;
  return typeof m.invocationId === "string" && m.invocationId !== "";
}

Type guard

function isWellFormedCompletion(m: any): boolean {
  if (!m) return false;
  if (m.result && m.error) return false;
  if (m.error && typeof m.error !== "string") return false;
  return typeof m.invocationId === "string" && m.invocationId.length > 0;
}

Prevention

When it happens

Trigger: Server returns a Completion message where: result and error are both set; error is set to an empty string or non-string; or invocationId is missing/empty. Also triggered by a malformed/injected frame.

Common situations: Server hub method throws but the error serializer emits an empty message, a proxy rewrites the payload dropping invocationId, or a hand-written server implementation that populates result and error together.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/a432ec2fb22f57db. Report an issue: GitHub.

Appendix: 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 3600ca084e)