Skip to content

RpcClientError loses socket error details due to hardcoded message and Cause.squash #5988

@schickling

Description

@schickling

Problem

When a socket error occurs in RpcClient.makeProtocolSocket, the RpcClientError is created with:

  1. A hardcoded generic message "Error in socket"
  2. Cause.squash(cause) which flattens the full cause tree

This results in error messages like:

Error: RpcClientError: Error in socket at ...

The underlying socket error details (like SocketCloseError with code, closeReason, or SocketGenericError with reason: "Open" | "Read" | "Write" | "OpenTimeout") are buried in the cause field and not surfaced in the error message.

Location

https://github.com/Effect-TS/effect/blob/main/packages/rpc/src/RpcClient.ts#L1010-L1014

currentError = new RpcClientError({
  reason: "Protocol",
  message: "Error in socket",  // <-- hardcoded, loses context
  cause: Cause.squash(cause)   // <-- flattens the cause tree
})

Proposed Solution

Extract socket error details and include them in the message:

const socketError = Cause.failureOption(cause)
const errorMessage = Option.isSome(socketError)
  ? `Socket ${socketError.value.reason}${
      'code' in socketError.value ? `: code ${socketError.value.code}` : ''
    }${
      'closeReason' in socketError.value && socketError.value.closeReason 
        ? ` (${socketError.value.closeReason})` 
        : ''
    }`
  : "Error in socket"

currentError = new RpcClientError({
  reason: "Protocol",
  message: errorMessage,
  cause: Cause.squash(cause)
})

This would produce messages like:

  • Socket Close: code 1006
  • Socket Open: connection refused
  • Socket OpenTimeout

Additional Consideration

The RpcClientError class itself uses the message field as a schema property, which shadows Error.message. Consider using note (or similar) for the schema field and computing get message() from the structured fields - similar to the pattern used for other tagged errors to preserve both structured data and a human-readable message.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions