unstable_cancel_request only.Expand description
Request cancellation with $/cancel_request.
The SDK exposes the ACP $/cancel_request notification behind the
unstable_cancel_request feature. The notification is protocol-level:
either side may send it to ask the peer to cancel one outstanding JSON-RPC
request by ID.
Cancellation is cooperative. A peer may ignore $/cancel_request, may
finish with normal data, or may respond to the original request with
Error::request_cancelled (-32800). The requesting side always
receives a response to the original request; cancellation only changes
which response that is. Unhandled $/-prefixed notifications are ignored
by the SDK (even without this feature), so peers that do not support
cancellation simply will not act on it.
§Cancelling outgoing requests
To cancel a request sent through ConnectionTo::send_request, keep the
returned SentRequest and call cancel on it:
let request = cx.send_request(MyRequest {});
request.cancel()?;
// The peer still responds to the request: with normal data if it raced
// ahead, or with the standard cancellation error.
let result = request.block_task().await;The SentRequest remembers the peer and any proxy wrapping used for the
original request, so this also works for requests sent through
ConnectionTo::send_request_to.
Dropping a SentRequest before the SDK receives a response also sends
$/cancel_request. This covers abandoned request handles and futures. For a
request whose eventual response should be ignored, but which should continue
running on the peer, call detach instead; the
eventual response is discarded, but no cancellation is sent. The peer is
still expected to answer the JSON-RPC request eventually; use a notification
instead when no response is expected at all. Once the SDK routes a response
for the request, automatic cancellation is disarmed: the peer has already
answered, even if caller code has not yet consumed the handle with
block_task, on_receiving_result, or forward_response_to, and even
if a dispatch handler claimed the response.
§Handling cancellation of incoming requests
For incoming requests, get the request-local cancellation marker from the
Responder. This keeps cancellation handling next to the request work it
controls:
let cancellation = responder.cancellation();
cx.spawn(async move {
let response = cancellation.run_until_cancelled(run_request(request)).await;
responder.respond_with_result(response)
})?;run_until_cancelled is the simple path for handlers that should stop
work and reply with the standard cancellation error as soon as cancellation
is requested; it drops the work future when cancellation wins. If the
handler needs cleanup, partial results, or custom cancellation behavior,
use cancelled or
is_cancelled directly inside the
request work instead.
Cancellation markers are only updated when the connection can process the
incoming $/cancel_request notification. Long-running handlers should
return quickly and move work into ConnectionTo::spawn, SentRequest
callbacks, or another task; see the ordering chapter.
§Proxies
When proxying with forward_response_to, the SDK observes the upstream
Responder cancellation marker and forwards cancellation to the
downstream request automatically. The downstream response (normal data or a
cancellation error) is still forwarded back upstream.
Because cancellation propagates per hop this way, the raw notification is
never tunneled across hops: ConnectionTo::send_proxied_message_to drops
$/cancel_request notifications rather than forwarding a requestId that
was allocated on a different connection and would be meaningless to the
next peer.
§Custom methods on proxies
A proxy that intercepts a method with its own handler decides what
cancellation means for it. The SDK always records the cancellation on the
request’s Responder marker before the handler chain runs; what happens
next is up to the handler that owns the request:
- Handle locally: react to
Responder::cancellationlike any request handler (ignore it, finish early, or respond withError::request_cancelled). - Forward and propagate: use
forward_response_to, or, when the forwarding needs custom logic (rewriting the request, post-processing the result), register the upstream marker explicitly withforward_cancellation_frombefore consuming the handle:
backend
.send_request(request)
.forward_cancellation_from(responder.cancellation())
.on_receiving_result(async move |result| {
// Custom result handling before responding upstream.
responder.respond_with_result(result)
})?;- Absorb: consume the handle without registering the marker
(
on_receiving_resultorblock_taskalone); the upstream marker is still set, but nothing is sent downstream and the request runs to completion there. - Custom routing: claim the
$/cancel_requestnotification itself in a handler (user handlers run before the generic forwarding fallbacks) and translate it manually when you control the relevant hop-local request IDs.
§Low-level access
Register CancelRequestNotification (or ProtocolLevelNotification)
directly only when you need low-level access to cancellation notifications,
such as custom routing or protocol tracing:
use agent_client_protocol::schema::v1::CancelRequestNotification;
let builder = UntypedRole.builder().on_receive_notification(
async |cancel: CancelRequestNotification, _cx: ConnectionTo<UntypedRole>| {
// Mark the matching in-flight operation cancelled.
let _request_id = cancel.request_id;
Ok(())
},
agent_client_protocol::on_receive_notification!(),
);Such a handler observes cancellation notifications but does not replace
the built-in handling: the SDK updates the Responder cancellation
markers for every incoming $/cancel_request before the handler chain
runs, even when a handler claims the notification.
If you are implementing custom routing and already know the JSON-RPC request
ID on the peer connection you are targeting, use
ConnectionTo::send_cancel_request_to. Most code should use
SentRequest::cancel instead, because the request handle already knows the
correct peer, request ID, and proxy wrapping.