wireStatusFor function
Translates an error thrown by a handler into what may go on the wire.
DEFAULT DENY: nothing reaches the caller unless it is provably safe to send. Two kinds qualify; everything else gets kInternalErrorWireMessage while the cause stays on the server, where every call site logs it with its stack.
- RpcStatusException — the handler SPEAKING to its caller. Status, message and details are all deliberate, so all three are forwarded intact. This is the supported way to say something to a peer.
- the RpcException hierarchy — rpc_dart's own diagnostics ("gRPC frame
payload is too large: N (max: M)") are what a peer needs in order to
correct itself. Only the MESSAGE is sent, never
toString(): the class is public, and a subclass may append more there (rpc_data'sRpcDataErrorappends its cause, a SQLite error with the statement).
Do NOT widen this to Exception. An exception is not safe merely because
the thrower chose to signal it: a database driver puts the failing query in
it, an HTTP client the URL and sometimes a token, dart:io the path or the
host. Nor can the leaky types be allow-listed -- most belong to packages
this library has never heard of. Error is no different: its text is
internal state, and it reached unauthenticated peers on all four call shapes
before the deny became the default.
RpcCancelledException and RpcDeadlineExceededException are not
special-cased: they live in a part of the contracts library, which this
file cannot import without a cycle, and the responder pipeline answers both
with their own status long before an error is translated.
Implementation
({RpcStatus status, String message, Uint8List? detailsBin}) wireStatusFor(
Object error,
) {
if (error is RpcStatusException) {
return (
status: error.statusCode,
message: error.message,
detailsBin: error.statusDetailsBin,
);
}
if (error is RpcException) {
return (
status: RpcStatus.internal,
message: error.message,
detailsBin: null,
);
}
return (
status: RpcStatus.internal,
message: kInternalErrorWireMessage,
detailsBin: null,
);
}