Error Handling
Errors thrown in handlers are automatically caught, serialized, and transmitted to the client as Arrow IPC error batches.
Throwing errors
Section titled “Throwing errors”Throw any Error from a handler to signal a failure:
import { Protocol, float } from "@query-farm/vgi-rpc";
const protocol = new Protocol("MyService");
protocol.unary("divide", { params: { a: float, b: float }, result: { result: float }, handler: async ({ a, b }) => { if (b === 0) throw new Error("Division by zero"); return { result: a / b }; },});The error message is transmitted to the client and the transport remains clean for subsequent requests.
Wire format
Section titled “Wire format”Errors are transmitted as zero-row Arrow batches with EXCEPTION-level metadata,
carrying the three layers of the error model (WIRE_PROTOCOL.md §8, adopted from
gRPC’s google.rpc.Status):
vgi_rpc.log_levelset toEXCEPTIONvgi_rpc.log_messagecontaining the error message ("{ExceptionType}: {message}")vgi_rpc.error_code— always present: one of gRPC’s sixteen canonical code names (UNAVAILABLE,NOT_FOUND, …). An error that declares none isUNKNOWN.vgi_rpc.error_kind— optional. The reason a client branches on, present when the thrown error carries anerrorKindmarker (see Typed error classes below).vgi_rpc.error_details— optional. A JSON array of typed details from a fixed catalog (vgi_rpc.RetryInfo,vgi_rpc.BadRequest, …). At most 4 KiB of UTF-8; a larger array, or one that repeats a type or invents avgi_rpc.*type, is dropped whole, never trimmed.vgi_rpc.log_extra— a JSON blob mirroring the three layers:
{ "exception_type": "ServerDrainingError", "exception_message": "server is draining", "error_code": "UNAVAILABLE", "error_kind": "server_draining", "error_details": [{"@type": "vgi_rpc.RetryInfo", "retry_delay_seconds": 1}], "traceback": "..."}Tracebacks
Section titled “Tracebacks”traceback is included by default on every transport — the DuckDB
extension shows the remote traceback in the error a user sees. One server-wide
switch turns it off everywhere: new VgiRpcServer(protocol, { includeTracebacks: false }) (or the HTTP handler’s includeTracebacks option). When on, it is
never empty. Type, message, code, kind and details are sent either way.
Choosing the code: StatusError
Section titled “Choosing the code: StatusError”Throw a StatusError to choose what a client sees:
import { retryInfo, StatusError } from "@query-farm/vgi-rpc";
throw new StatusError("report is being rebuilt", { code: "UNAVAILABLE", kind: "report_rebuilding", details: [retryInfo(30)],});Any error class may instead declare errorCode / errorKind / errorDetails
properties (instance or static). Details must come from the catalog; a
protocol-specific type lives under the protocol’s own name
(acme.Reports.v1.SomeDetail).
HTTP transport
Section titled “HTTP transport”Over the HTTP transport the error category is also surfaced as the
X-VGI-RPC-Error response header (the RPC_ERROR_HEADER constant), so clients
can inspect it without reading the Arrow body.
Error classes
Section titled “Error classes”RpcError
Section titled “RpcError”Represents an error received from the RPC protocol:
import { RpcError } from "@query-farm/vgi-rpc";
class RpcError extends Error { readonly errorType: string; readonly errorMessage: string; readonly remoteTraceback: string; // "" only when the server turned tracebacks off readonly errorCode: string; // "" only from a server older than the model readonly errorKind: string; // "" when absent readonly errorDetails: readonly ErrorDetailJson[]; // as received, unknown types included readonly requestId: string;
get code(): ErrorCode; // UNKNOWN when absent or unrecognised isRetryable(): boolean; details(): ErrorDetail[]; // catalog entries only retryInfo(): RetryInfo | null; // also errorInfo(), badRequest(), preconditionFailure(), // quotaFailure(), resourceInfo(), help(), localizedMessage()}Every client decode path — pipe, HTTP unary, stream init and exchange, and an
externalized error batch — fills these fields. isRetryable() is a
classification, not a policy: UNAVAILABLE is retryable, RESOURCE_EXHAUSTED
only with RetryInfo, everything else is final. The client never retries an
RPC error automatically, because a method may not be idempotent.
VersionError
Section titled “VersionError”Thrown when the client sends an unsupported request version:
import { VersionError } from "@query-farm/vgi-rpc";
class VersionError extends Error { // Inherits message from Error}Typed error classes
Section titled “Typed error classes”Several built-in error classes carry a stable errorKind marker that is hoisted
onto the error batch as the vgi_rpc.error_kind metadata key (and into the
X-VGI-RPC-Error header over HTTP). Throw these from a handler to signal a
specific, machine-readable condition. The corresponding ERROR_KIND_* string
constants are exported for matching on the client side.
import { MethodNotImplementedError, ServerDrainingError, SessionLostError, ERROR_KIND_METHOD_NOT_IMPLEMENTED, ERROR_KIND_SERVER_DRAINING, ERROR_KIND_SESSION_LOST,} from "@query-farm/vgi-rpc";| Class | errorKind value |
Constant | Meaning |
|---|---|---|---|
MethodNotImplementedError |
method_not_implemented |
ERROR_KIND_METHOD_NOT_IMPLEMENTED |
The client invoked a method the server does not implement. |
ServerDrainingError |
server_draining |
ERROR_KIND_SERVER_DRAINING |
A session was requested while the server is draining. |
SessionLostError |
session_lost |
ERROR_KIND_SESSION_LOST |
A sticky session token is malformed, expired, evicted, or bound to a different worker / principal (HTTP only). |
ProtocolVersionError
Section titled “ProtocolVersionError”ProtocolVersionError extends VersionError and is thrown by the server when
the client’s declared vgi_rpc.protocol_version is incompatible with the
server’s (exact major + minor match required; patch ignored). It carries the
protocol_version_mismatch error kind and a directional message indicating
which side to upgrade. Because it subclasses VersionError, existing catch
sites continue to write a typed error stream and keep serving. It is not
exported from the package index — it is produced internally by the server.
Streaming errors
Section titled “Streaming errors”Errors in producer produce and exchange exchange functions terminate the stream and send an error batch to the client. The init function errors are also caught and reported.
