pub enum DispatchError {
Show 15 variants
UnknownTool,
NotCallable,
UnknownMethod,
UnknownResource,
UnknownPrompt,
HandlerFailed(String),
MalformedParams,
MalformedInitialize,
UnnegotiableVersion,
MalformedRequestMeta,
UnsupportedVersion {
requested: String,
supported: Vec<String>,
},
DuplicateInFlight(String),
MissingClientCapability {
required: Vec<&'static str>,
},
TamperedRequestState,
IdempotencyUnavailable(String),
}Expand description
Why a tool call did not run.
Variants§
UnknownTool
No tool of that name is registered.
Deliberately the same answer for a tool that exists but is hidden from this session: the difference would let a client probe for the existence of a tool it may not see. The server still logs which it was.
NotCallable
The tool is registered, but the session did not negotiate the features it requires.
UnknownMethod
No method of that name exists.
Refused on the method, not on a tool, because the caller named a method and there is no tool to say anything about.
UnknownResource
No resource of that URI is registered, or it is listed without a reader.
Two causes share one answer because a peer cannot tell them apart: a
listing is a promise, so a resource that appears in resources/list
and then refuses to be read is the same failure as one that was never
there, and naming which of the two happened tells the peer nothing it
can act on.
UnknownPrompt
No prompt of that name is registered, or it is listed without a getter.
HandlerFailed(String)
A registered reader or getter refused, naming why.
Carries the handler’s own message rather than a category, because the peer is a client and the only thing it can do with a category is show it to a user who would rather read what went wrong.
MalformedParams
tools/call params were not the shape a call has to be: not an object,
no string name, or an arguments that is not an object.
A distinct variant from UnknownTool because a call with no name at
all has not failed to find a tool — it never named one, and answering
“no such tool” would send the caller looking for a tool that works.
MalformedInitialize
initialize params were not the shape the handshake has to be.
Separate from MalformedParams because the
two failures are not the same event: that one says a tool call named
nothing, and this one says the one request that establishes a session
arrived in a shape that would have to be guessed at. A peer sending it
has a bug worth naming, not a tool to go looking for.
Covers a body that is not an object, a missing or non-string
protocolVersion, a string that is not a protocol version, a missing or
non-object capabilities, and a clientInfo with no name.
UnnegotiableVersion
The session is on a revision this build has no wire types for.
Distinct from a refused method (UnknownMethod)
because nothing about the request was wrong: the method exists, the
params parse, and the answer cannot be built because the revision the
session settled on is one negotiate_server would have to counter-
offer. The session should never be in this state — it is reported
rather than papered over, because the alternative is emitting a result
whose shape the revision cannot express.
MalformedRequestMeta
A SEP-2575 request arrived without the _meta the schema requires.
2026-07-28 makes RequestParams._meta required on every request and
requires two keys in it — the revision and the client’s capabilities —
because there is no handshake to carry them and no session to remember
them on. A request missing either is a well-formed message with a
missing argument, which is -32602 and not a transport refusal.
Its own variant rather than a reuse of
MalformedInitialize because the two are
different failures with different fixes: one says the handshake arrived
in a shape that would have to be guessed at, this says the shape is
right and a required key is not there.
UnsupportedVersion
A SEP-2575 request named a revision this build does not implement.
Not MalformedParams, and the difference is
the whole point of the code the specification assigns: -32022 with
data.supported is what lets a client retry against a version this
server does speak, where -32602 would send it looking for a bug in
its own request.
Fields
DuplicateInFlight(String)
A second request arrived with an id this session is still working on.
The JSON-RPC reading, and the reason it is an error rather than a
queue: the first request is a real, running, side-effecting call, and
the second carries no way to say what should happen to it. Running the
tool twice is the answer a retry gets by accident, and this is the
answer it gets on purpose. A client that wants the first result waits
for it and retries later — at which point the id is Completed and
comes straight back.
MissingClientCapability
Serving the request needs client capabilities the request did not
declare (SEP-2575, -32021).
Raised when a handler answers with an InputRequiredResult asking for
something the client said it cannot do: sending it anyway would ask a
client to sample or elicit after it declined to, and the specification
is explicit that a server “MUST NOT rely on capabilities the client has
not declared”.
TamperedRequestState
A SEP-2322 requestState did not verify.
-32602, because the state is a request parameter and this one is not a
value this server issued. Refused before the handler runs, so no
handler can be made to act on a state a client wrote.
The store holding recorded results could not be reached.
Reported rather than swallowed because the alternative is the one thing this whole path exists to prevent: a store that errors, a caller that treats the error as “no claim”, and a tool that runs a second time. Failing the call is the only answer that does not charge a client twice behind its back.
Implementations§
Source§impl DispatchError
impl DispatchError
Sourcepub const fn jsonrpc_code(&self) -> IrErrorCode
pub const fn jsonrpc_code(&self) -> IrErrorCode
The JSON-RPC code this refusal is reported as.
Lives here rather than in a transport adapter because it is a property of the error, not of the way it is written down. It sat in the stdio adapter until the HTTP router needed the same mapping, and the two options at that point were both wrong: copy it and let the copies drift apart, or reach into another adapter and break the layering.
Every refusal gets an answer. The alternative is to drop it, which
leaves the peer waiting on a response that is never coming — and a
server that hangs is worse than one that says no. The codes are the
closest the standard offers: a method nobody serves is -32601, a call
that does not name something callable is -32602, and a session that
did not negotiate a feature — or one settled on a revision this build
cannot serve — is a server-side condition rather than either.
A malformed initialize is -32602 like any other malformed params:
the peer sent a request whose body it could not have meant, which is the
same condition whatever the method was.
§Examples
assert_eq!(
DispatchError::UnknownMethod.jsonrpc_code(),
IrErrorCode::MethodNotFound,
);
assert_eq!(
DispatchError::MalformedParams.jsonrpc_code(),
IrErrorCode::InvalidParams,
);Sourcepub fn wire_data(&self) -> Option<JsonObject>
pub fn wire_data(&self) -> Option<JsonObject>
The data member this refusal’s code requires, if it has one.
-32022 carries the revision the client asked for and every revision
this build speaks; -32021 carries requiredCapabilities as a
ClientCapabilities object keyed by what is missing — not an array of
names, because the schema types it as the object a client declares.
Every other refusal carries none. One definition, so stdio and HTTP
cannot disagree about it.
Sourcepub fn as_wire(&self) -> (IrErrorCode, String)
pub fn as_wire(&self) -> (IrErrorCode, String)
The code and message pair a transport writes to the wire.
The message is thiserror’s own for the variant, never the method
name the peer chose: a method name is a JSON string a peer controls, and
JSON permits a newline inside one, so echoing it into a log line would
let a peer close that line and open one of its own.
Trait Implementations§
Source§impl Clone for DispatchError
impl Clone for DispatchError
Source§impl Debug for DispatchError
impl Debug for DispatchError
Source§impl Display for DispatchError
impl Display for DispatchError
impl Eq for DispatchError
Source§impl Error for DispatchError
impl Error for DispatchError
1.30.0 · Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()
Source§impl From<IdempotencyError> for DispatchError
impl From<IdempotencyError> for DispatchError
Source§fn from(error: IdempotencyError) -> Self
fn from(error: IdempotencyError) -> Self
Keeps a poisoned or absent store from reading as an absent claim.
Source§impl From<OutcomeError> for DispatchError
impl From<OutcomeError> for DispatchError
Source§fn from(error: OutcomeError) -> Self
fn from(error: OutcomeError) -> Self
A reader or getter that could not produce a document failed.
Every variant becomes DispatchError::HandlerFailed carrying the
outcome’s own message. None of them is a bad request — the peer named a
resource or a prompt that exists and sent well-formed params — so
reporting any of them as InvalidParams would send a client looking in
its own request for something it got right.