can1357/oh-my-pi · error · SyntaxError

Malformed JSON-RPC error response

Error message

Malformed JSON-RPC error response

What it means

The JSON-RPC response contained an `error` member, but the error object itself was malformed — not an object, or its code is not a number / message is not a string as JSON-RPC 2.0 requires. The transport throws SyntaxError("Malformed JSON-RPC error response") instead of fabricating a JSON-RPC error from invalid data.

Source

Thrown at packages/coding-agent/src/mcp/transports/http.ts:511

			// Handle SSE response
			if (contentType.includes("text/event-stream")) {
				return this.#parseSSEResponse<T>(response, id, options);
			}

			stage = "decode";
			// Handle JSON response
			const result: unknown = await response.json();
			if (!isRecord(result) || result.jsonrpc !== "2.0" || (!("result" in result) && !("error" in result))) {
				throw new SyntaxError("Malformed JSON-RPC response");
			}
			if (result.error !== undefined) {
				if (
					!isRecord(result.error) ||
					typeof result.error.code !== "number" ||
					typeof result.error.message !== "string"
				) {
					throw new SyntaxError("Malformed JSON-RPC error response");
				}
				throw createMCPJsonRpcError(
					"http",
					{ code: result.error.code, message: result.error.message, data: result.error.data },
					traceId,
				);
			}

			return result.result as T;
		} catch (error) {
			if (operation.isTimeoutAbort(error) || operation.timedOut()) {
				throw new MCPTransportError({
					transport: "http",
					stage,
					failure: "timeout",
					message: `Request timeout after ${timeout}ms`,
					retryable: false,
					traceId,

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect server logs for the original error and fix the server's JSON-RPC error serialization (error must be {code:number, message:string, data?})
  2. Align client and server on the same MCP/JSON-RPC protocol version
  3. If a proxy transforms error bodies, bypass or fix the transformation
  4. Catch the normalized decode-stage transport error and surface it to the user with trace context

Example fix

// before
// server: {"jsonrpc":"2.0","id":1,"error":"boom"}
// after
// server: {"jsonrpc":"2.0","id":1,"error":{"code":-32603,"message":"boom"}}
Defensive patterns

Strategy: type-guard

Validate before calling

// Conformance-test the server's error path before production:
const res = await callToolThatAlwaysFails();
const e = res.error;
if (typeof e !== "object" || typeof e?.code !== "number" || typeof e?.message !== "string") throw new Error("server emits malformed JSON-RPC errors");

Type guard

function isJsonRpcError(e: unknown): e is { code: number; message: string; data?: unknown } {
  return typeof e === "object" && e !== null && typeof (e as any).code === "number" && typeof (e as any).message === "string";
}

Try / catch

try {
  await transport.request(method, params);
} catch (err) {
  if (err instanceof SyntaxError && err.message === "Malformed JSON-RPC error response") {
    log.error("server JSON-RPC error serialization is non-conformant");
  } else throw err;
}

Prevention

When it happens

Trigger: Server sends {jsonrpc:"2.0", id, error: ...} where error is a string, null, an object with non-numeric code (e.g. "E123"), or missing/non-string message.

Common situations: Server-side error serialization bugs; custom middleware converting errors to strings; protocol-version mismatch where the server emits a non-standard error shape; frameworks auto-wrapping exceptions incorrectly.

Understand the failure class

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/9c23b778f6e17524. Report an issue: GitHub.