From b9b482a0f0ae84587b6a2a6a7828f3c3c9c29764 Mon Sep 17 00:00:00 2001 From: coyaSONG <66289470+coyaSONG@users.noreply.github.com> Date: Fri, 17 Jul 2026 10:48:48 +0900 Subject: [PATCH] include socket details in RpcClient errors --- .changeset/tidy-sockets-report.md | 5 ++++ packages/rpc/src/RpcClient.ts | 5 +++- packages/rpc/test/RpcClient.test.ts | 39 +++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 .changeset/tidy-sockets-report.md create mode 100644 packages/rpc/test/RpcClient.test.ts diff --git a/.changeset/tidy-sockets-report.md b/.changeset/tidy-sockets-report.md new file mode 100644 index 00000000000..d2b0bf9c810 --- /dev/null +++ b/.changeset/tidy-sockets-report.md @@ -0,0 +1,5 @@ +--- +"@effect/rpc": patch +--- + +Include socket error details in RpcClient protocol error messages. diff --git a/packages/rpc/src/RpcClient.ts b/packages/rpc/src/RpcClient.ts index 1b5e59b4661..330885d25f7 100644 --- a/packages/rpc/src/RpcClient.ts +++ b/packages/rpc/src/RpcClient.ts @@ -1013,7 +1013,10 @@ export const makeProtocolSocket = (options?: { } currentError = new RpcClientError({ reason: "Protocol", - message: "Error in socket", + message: Option.match(error, { + onNone: () => "Error in socket", + onSome: (error) => `Socket ${error.message}` + }), cause: Cause.squash(cause) }) return writeResponse({ diff --git a/packages/rpc/test/RpcClient.test.ts b/packages/rpc/test/RpcClient.test.ts new file mode 100644 index 00000000000..83b8aca5b6c --- /dev/null +++ b/packages/rpc/test/RpcClient.test.ts @@ -0,0 +1,39 @@ +import * as Socket from "@effect/platform/Socket" +import type { RpcClientError } from "@effect/rpc" +import { RpcClient, RpcSerialization } from "@effect/rpc" +import { assert, describe, it } from "@effect/vitest" +import { Deferred, Effect, Schedule } from "effect" + +describe("RpcClient", () => { + it.effect("includes socket close details in protocol errors", () => + Effect.scoped(Effect.gen(function*() { + const socketError = new Socket.SocketCloseError({ + reason: "Close", + code: 1006, + closeReason: "connection lost" + }) + const socket: Socket.Socket = { + [Socket.TypeId]: Socket.TypeId, + run: () => Effect.fail(socketError), + runRaw: () => Effect.fail(socketError), + writer: Effect.succeed(() => Effect.void) + } + const errorLatch = yield* Deferred.make() + const protocol = yield* RpcClient.makeProtocolSocket({ + retrySchedule: Schedule.stop + }).pipe( + Effect.provideService(Socket.Socket, socket), + Effect.provideService(RpcSerialization.RpcSerialization, RpcSerialization.json) + ) + + yield* protocol.run((response) => + response._tag === "ClientProtocolError" + ? Deferred.succeed(errorLatch, response.error) + : Effect.void + ).pipe(Effect.forkScoped) + + const error = yield* Deferred.await(errorLatch) + assert.strictEqual(error.message, "Socket Close: 1006: connection lost") + assert.strictEqual(error.cause, socketError) + }))) +})