ruvnet/ruflo · error · Error

MCP init failed

Error message

MCP init failed

What it means

Fallback message thrown in the worker's ensureLoaded() when the WASM MCP server's initialize handle_message returned a JSON-RPC error whose .message is itself missing/falsy (initRes.error.message ?? "MCP init failed"). It indicates the initialize handshake failed inside the worker and the server did not provide a human-readable reason.

Source

Thrown at ruflo/src/ruvocal/src/lib/wasm/wasm.worker.ts:266

	loadPromise = (async () => {
		// TODO: load real rvagent_wasm.js via dynamic import once the static
		// bundle exposes a worker-friendly init. Mock is functionally complete
		// for the chat-ui's MCP integration test surface.
		mcpServer = createMockServer();
		gallery = createMockGallery();

		const initReq = JSON.stringify({
			jsonrpc: "2.0",
			id: 1,
			method: "initialize",
			params: {
				protocolVersion: "2024-11-05",
				clientInfo: { name: "ruvocal-ui-worker", version: "1.0.0" },
			},
		});
		const initRes = JSON.parse(mcpServer.handle_message(initReq));
		if (initRes.error) throw new Error(initRes.error.message ?? "MCP init failed");
	})();

	await loadPromise;
}

ctx.addEventListener("message", async (event: MessageEvent<WorkerRequest>) => {
	const { id, method, params } = event.data;

	try {
		await ensureLoaded();
		if (!mcpServer || !gallery) throw new Error("WASM not initialized");

		switch (method) {
			case "load":
				reply(id, true);
				return;

			case "callMcp": {

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Log the full initRes object (code + data) before the throw to recover the real reason.
  2. Confirm protocolVersion "2024-11-05" and clientInfo { name, version } match the server's expectations.
  3. Rebuild the worker glue and WASM binary together so handle_message's error contract is consistent.
  4. If the underlying cause is transient (worker cold-start under load), retry ensureLoaded once before surfacing the error.

Example fix

// before
const initRes = JSON.parse(mcpServer.handle_message(initReq));
if (initRes.error) throw new Error(initRes.error.message ?? "MCP init failed");
// after
const initRes = JSON.parse(mcpServer.handle_message(initReq));
if (initRes.error) {
  throw new Error(`MCP init failed: ${initRes.error.message ?? `code=${initRes.error.code} data=${JSON.stringify(initRes.error.data)}`}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

const PROTOCOL = "2024-11-05";
function validInitParams(p: unknown): boolean {
  return (
    !!p &&
    typeof (p as { protocolVersion?: string }).protocolVersion === "string" &&
    typeof (p as { clientInfo?: { name?: string; version?: string } }).clientInfo?.name === "string" &&
    typeof (p as { clientInfo?: { name?: string; version?: string } }).clientInfo?.version === "string"
  );
}
if (!validInitParams({ protocolVersion: PROTOCOL, clientInfo: { name: "ruvocal-ui-worker", version: "1.0.0" } })) {
  throw new Error("invalid initialize params");
}

Type guard

function hasRpcError(r: unknown): r is { error: { code?: number; message?: string; data?: unknown } } {
  return typeof r === "object" && r !== null && "error" in (r as object) && typeof (r as { error: unknown }).error === "object";
}

Try / catch

try {
  await ensureLoaded();
} catch (e) {
  const msg = String((e as Error)?.message ?? e);
  if (msg === "MCP init failed" || msg.startsWith("MCP init failed")) {
    // re-load the worker module once, then surface a richer error with initRes.error.code
    await reloadWorker();
    await ensureLoaded();
  } else throw e;
}

Prevention

When it happens

Trigger: Worker constructs the (mock) MCP server, sends the initialize JSON-RPC frame, and the parsed response has an error object with no message field — e.g. { error: { code: -32603 } } — so the nullish-coalescing falls through to the generic string.

Common situations: Worker WASM/glue version skew; the mock mcpServer.handle_message throws internally and the wrapper produces a code-only error; protocolVersion or clientInfo shape rejected without a descriptive message; worker memory/resource limits causing an opaque failure.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/137a52bea9f33c17. Report an issue: GitHub.