A node-bound resource is being served over a transport that isn't in its node's declared
ProtocolKind set. Because a client derives its transport from the node's declared
transports, serving it over one the node doesn't advertise (e.g. a WebSocket-only node served on
an httpServer) means every client dials a protocol the server never answers — the silent "blank
dashboard / no live data" failure. The server refuses to boot with this instead, so the mismatch is
loud and immediate. A multi-protocol node served over any of its declared transports passes.
export class class ProtocolKindMismatchclass ProtocolKindMismatch {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
resource: string;
declared: ReadonlyArray<ProtocolKind>;
servedOver: ProtocolKind;
}
A node-bound resource is being served over a transport that isn't in its node's declared
ProtocolKind
set. Because a client derives its transport from the node's declared
transports, serving it over one the node doesn't advertise (e.g. a WebSocket-only node served on
an httpServer) means every client dials a protocol the server never answers — the silent "blank
dashboard / no live data" failure. The server refuses to boot with this instead, so the mismatch is
loud and immediate. A multi-protocol node served over any of its declared transports passes.
ProtocolKindMismatch extends import DataData.const TaggedError: <"ProtocolKindMismatch">(tag: "ProtocolKindMismatch") => new <A>(args: VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & {
readonly _tag: "ProtocolKindMismatch";
} & Readonly<A>
Creates a tagged error class with a _tag discriminator.
When to use
Use when you need domain errors with discriminated-union handling.
Details
Like
Error
, but instances also carry a readonly _tag property,
enabling Effect.catchTag and Effect.catchTags for tag-based recovery.
The _tag is excluded from the constructor argument. Yielding an instance
inside Effect.gen fails the effect with this error.
Example (Recovering by tag)
import { Data, Effect } from "effect"
class NotFound extends Data.TaggedError("NotFound")<{
readonly resource: string
}> {}
class Forbidden extends Data.TaggedError("Forbidden")<{
readonly reason: string
}> {}
const program = Effect.gen(function*() {
return yield* new NotFound({ resource: "/users/42" })
})
const recovered = program.pipe(
Effect.catchTag("NotFound", (e) =>
Effect.succeed(`missing: ${e.resource}`))
)
TaggedError("ProtocolKindMismatch")<{
readonly resource: stringresource: string;
readonly declared: ReadonlyArray<ProtocolKind>declared: interface ReadonlyArray<T>ReadonlyArray<type ProtocolKind =
| "Http"
| "WebSocket"
| "IpcSocket"
The transport a
Node
speaks — tag-style names (apps rarely type this alias; they write
the literals or get inference from url / path):
"Http" — RpcClient.layerProtocolHttp (servers / CLIs)
"WebSocket" — browser WS (layerProtocolWebsocket / client layerProtocolSocket over WS)
"IpcSocket" — Unix-domain socket (same-machine; see
ipcServer
)
Stamped on the node so the topology is self-describing about how to reach it — connect/client
derive the transport from it. Inferred from a ws(s):// url, an http target, or { path } →
IpcSocket; otherwise declare it explicitly.
ProtocolKind>;
readonly servedOver: ProtocolKindservedOver: type ProtocolKind =
| "Http"
| "WebSocket"
| "IpcSocket"
The transport a
Node
speaks — tag-style names (apps rarely type this alias; they write
the literals or get inference from url / path):
"Http" — RpcClient.layerProtocolHttp (servers / CLIs)
"WebSocket" — browser WS (layerProtocolWebsocket / client layerProtocolSocket over WS)
"IpcSocket" — Unix-domain socket (same-machine; see
ipcServer
)
Stamped on the node so the topology is self-describing about how to reach it — connect/client
derive the transport from it. Inferred from a ws(s):// url, an http target, or { path } →
IpcSocket; otherwise declare it explicitly.
ProtocolKind;
}> {
override get ProtocolKindMismatch.message: stringmessage() {
const const serverFor: (
k: ProtocolKind
) => string
serverFor = (k: ProtocolKindk: type ProtocolKind =
| "Http"
| "WebSocket"
| "IpcSocket"
The transport a
Node
speaks — tag-style names (apps rarely type this alias; they write
the literals or get inference from url / path):
"Http" — RpcClient.layerProtocolHttp (servers / CLIs)
"WebSocket" — browser WS (layerProtocolWebsocket / client layerProtocolSocket over WS)
"IpcSocket" — Unix-domain socket (same-machine; see
ipcServer
)
Stamped on the node so the topology is self-describing about how to reach it — connect/client
derive the transport from it. Inferred from a ws(s):// url, an http target, or { path } →
IpcSocket; otherwise declare it explicitly.
ProtocolKind): string =>
k: ProtocolKindk === "Http" ? "httpServer" : k: ProtocolKindk === "WebSocket" ? "wsServer" : "ipcServer";
const const kinds: stringkinds = this.declared: ReadonlyArray<ProtocolKind>declared.ReadonlyArray<ProtocolKind>.join(separator?: string): stringAdds all the elements of an array separated by the specified separator string.
join(" / ");
const const servers: stringservers = this.declared: ReadonlyArray<ProtocolKind>declared.ReadonlyArray<ProtocolKind>.map<string>(callbackfn: (value: ProtocolKind, index: number, array: readonly ProtocolKind[]) => string, thisArg?: any): string[]Calls a defined callback function on each element of an array, and returns an array that contains the results.
map((k: ProtocolKindk) => `Node.${const serverFor: (
k: ProtocolKind
) => string
serverFor(k: ProtocolKindk)}`).Array<string>.join(separator?: string): stringAdds all the elements of an array into a string, separated by the specified separator string.
join(" / ");
return `Hyperlink "${this.resource: stringresource}" declares its node over ${const kinds: stringkinds}, but is being served over ${this.servedOver: ProtocolKindservedOver} — a client dials one of [${const kinds: stringkinds}] and would never reach it. Serve it over a declared transport (${const servers: stringservers}), or add ${this.servedOver: ProtocolKindservedOver} to the node.`;
}
}