Summary
Java ADK currently lacks a mechanism to cancel/abort a running agent invocation. TypeScript ADK already ships mature AbortSignal-based cancellation (docs/runtime/cancel.md), and Python ADK has active work in progress (google/adk-python#4825, google/adk-python#5983). Java ADK has no equivalent — no API, no internal checkpoints, and no graceful shutdown path.
Motivation
Cancellation is essential for production agent deployments:
- User-facing "Stop" button — long-running multi-tool loops need immediate, graceful termination when the user clicks stop.
- Timeout enforcement — server-side request deadlines (e.g., HTTP gateway timeouts) must propagate into the agent execution.
- Resource cleanup — without cooperative cancellation, in-flight LLM streaming connections and tool executions leak resources.
- Cost control — aborting early avoids unnecessary LLM token consumption.
Current State
Language | Status | Mechanism
-- | -- | --
TypeScript | ✅ Shipped | runner.runAsync(params, { signal }) with AbortSignal; checkpoints in LLM call, tool execution, and plugin callbacks; graceful generator completion (no throw)
Python | 🚧 In progress |
google/adk-python#4825 (stop_event: asyncio.Event),
google/adk-python#5983 (/cancel API + task.cancel())
Java | ❌ Missing | Only InvocationContext.endInvocation (internal flag, not externally triggerable); Disposable.dispose() is a hard RxJava cut (not graceful, tools don't check it); no RunConfig timeout/cancel field
Proposed Design (aligned with TypeScript)
API Surface
// Option A: CancellationToken (similar to .NET / structured concurrency)
CancellationTokenSource cts = new CancellationTokenSource();
RunConfig config = RunConfig.builder()
.cancellationToken(cts.token())
.build();
Flowable<Event> events = runner.runAsync(userId, sessionId, content, config);
// Later:
cts.cancel(); // triggers graceful shutdown
// Option B: RxJava-native (Disposable already exists, but needs cooperative checks)
// Enhance internal checkpoints to check subscription disposal state
<clipboard-copy aria-label="Copy code to clipboard" class="ClipboardButton btn js-clipboard-copy m-2 p-0" data-copy-feedback="Copied!" data-tooltip-direction="w" value="// Option A: CancellationToken (similar to .NET / structured concurrency)
CancellationTokenSource cts = new CancellationTokenSource();
RunConfig config = RunConfig.builder()
.cancellationToken(cts.token())
.build();
Flowable<Event> events = runner.runAsync(userId, sessionId, content, config);
// Later:
cts.cancel(); // triggers graceful shutdown
// Option B: RxJava-native (Disposable already exists, but needs cooperative checks)
// Enhance internal checkpoints to check subscription disposal state" tabindex="0" role="button" style="box-sizing: border-box; margin: 8px !important; padding: 0px !important; font-size: 14px; font-weight: 500; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; appearance: none; border: 1px solid rgb(61, 68, 77); border-radius: 6px; line-height: 20px; display: inline-block; position: relative; color: rgb(240, 246, 252); background-color: rgb(33, 40, 48); box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color 80ms cubic-bezier(0.33, 1, 0.68, 1), box-shadow 80ms cubic-bezier(0.33, 1, 0.68, 1), border-color 80ms cubic-bezier(0.33, 1, 0.68, 1);">
Internal Checkpoints (minimum)
Following TypeScript's lead, cancellation should be checked at:
- Before each LLM call —
BaseLlmFlow.runOneStep() (already checks endInvocation, easy to extend) - Before each tool execution —
Functions.handleFunctionCalls() (currently has NO cancellation check) - During LLM streaming — between chunks (for long generations)
- Plugin/callback boundaries —
beforeModel / afterModel / beforeTool / afterTool
Semantics (align with TypeScript cancel.md)
- Graceful: the Flowable completes normally (no error signal), already-emitted events are preserved in the session.
- Cooperative: cancellation is checked at defined points; in-flight atomic operations (single LLM call, single tool run) may complete before the next checkpoint.
- Idempotent: calling cancel multiple times is safe.
Questions for Maintainers
- API preference:
CancellationToken (explicit, testable) vs. leveraging RxJava Disposable semantics (less new API surface but less portable)? - Scope: Should cancellation also cover
runLive (streaming/live mode), or start with runAsync only? - Event emission on cancel: Should the framework emit a final "cancelled" event (like Python's
temp:cancelled state proposal), or just complete silently (like TypeScript)?
References
I'm happy to contribute the implementation once the API direction is agreed upon. I have production experience implementing cooperative cancellation in a Java ADK-based agent platform (checkpoint-based, with checks at beforeModel/beforeTool/afterEvent boundaries).
Summary
Java ADK currently lacks a mechanism to cancel/abort a running agent invocation. TypeScript ADK already ships mature AbortSignal-based cancellation (
docs/runtime/cancel.md), and Python ADK has active work in progress (
google/adk-python#4825,
google/adk-python#5983). Java ADK has no equivalent — no API, no internal checkpoints, and no graceful shutdown path.
Motivation
Cancellation is essential for production agent deployments:
User-facing "Stop" button — long-running multi-tool loops need immediate, graceful termination when the user clicks stop.
Timeout enforcement — server-side request deadlines (e.g., HTTP gateway timeouts) must propagate into the agent execution.
Resource cleanup — without cooperative cancellation, in-flight LLM streaming connections and tool executions leak resources.
Cost control — aborting early avoids unnecessary LLM token consumption.
Current State
Language Status Mechanism
TypeScript ✅ Shipped runner.runAsync(params, { signal }) with AbortSignal; checkpoints in LLM call, tool execution, and plugin callbacks; graceful generator completion (no throw)
Python 🚧 In progress google/adk-python#4825 (stop_event: asyncio.Event), google/adk-python#5983 (/cancel API + task.cancel())
Java ❌ Missing Only InvocationContext.endInvocation (internal flag, not externally triggerable); Disposable.dispose() is a hard RxJava cut (not graceful, tools don't check it); no RunConfig timeout/cancel field
Proposed Design (aligned with TypeScript)
API Surface
// Option A: CancellationToken (similar to .NET / structured concurrency)
CancellationTokenSource cts = new CancellationTokenSource();
RunConfig config = RunConfig.builder()
.cancellationToken(cts.token())
.build();
Flowable events = runner.runAsync(userId, sessionId, content, config);
// Later:
cts.cancel(); // triggers graceful shutdown
// Option B: RxJava-native (Disposable already exists, but needs cooperative checks)
// Enhance internal checkpoints to check subscription disposal state
Internal Checkpoints (minimum)
Following TypeScript's lead, cancellation should be checked at:
Before each LLM call — BaseLlmFlow.runOneStep() (already checks endInvocation, easy to extend)
Before each tool execution — Functions.handleFunctionCalls() (currently has NO cancellation check)
During LLM streaming — between chunks (for long generations)
Plugin/callback boundaries — beforeModel / afterModel / beforeTool / afterTool
Semantics (align with TypeScript cancel.md)
Graceful: the Flowable completes normally (no error signal), already-emitted events are preserved in the session.
Cooperative: cancellation is checked at defined points; in-flight atomic operations (single LLM call, single tool run) may complete before the next checkpoint.
Idempotent: calling cancel multiple times is safe.
Questions for Maintainers
API preference: CancellationToken (explicit, testable) vs. leveraging RxJava Disposable semantics (less new API surface but less portable)?
Scope: Should cancellation also cover runLive (streaming/live mode), or start with runAsync only?
Event emission on cancel: Should the framework emit a final "cancelled" event (like Python's temp:cancelled state proposal), or just complete silently (like TypeScript)?
References
TypeScript ADK cancel docs: https://google.github.io/adk-docs/runtime/cancel/
Python ADK cancellation PR: google/adk-python#4825
Python ADK /cancel API PR: google/adk-python#5983
Python ADK cancellation discussion: google/adk-python#4156
I'm happy to contribute the implementation once the API direction is agreed upon. I have production experience implementing cooperative cancellation in a Java ADK-based agent platform (checkpoint-based, with checks at beforeModel/beforeTool/afterEvent boundaries).
Summary
Java ADK currently lacks a mechanism to cancel/abort a running agent invocation. TypeScript ADK already ships mature
AbortSignal-based cancellation (docs/runtime/cancel.md), and Python ADK has active work in progress (google/adk-python#4825, google/adk-python#5983). Java ADK has no equivalent — no API, no internal checkpoints, and no graceful shutdown path.Motivation
Cancellation is essential for production agent deployments:
Current State
Language | Status | Mechanism -- | -- | -- TypeScript | ✅ Shipped | runner.runAsync(params, { signal }) with AbortSignal; checkpoints in LLM call, tool execution, and plugin callbacks; graceful generator completion (no throw) Python | 🚧 In progress | google/adk-python#4825 (stop_event: asyncio.Event), google/adk-python#5983 (/cancel API + task.cancel()) Java | ❌ Missing | Only InvocationContext.endInvocation (internal flag, not externally triggerable); Disposable.dispose() is a hard RxJava cut (not graceful, tools don't check it); no RunConfig timeout/cancel fieldProposed Design (aligned with TypeScript)
API Surface
CancellationTokenSource cts = new CancellationTokenSource();
RunConfig config = RunConfig.builder()
.cancellationToken(cts.token())
.build();
Flowable<Event> events = runner.runAsync(userId, sessionId, content, config);
// Later:
cts.cancel(); // triggers graceful shutdown
// Option B: RxJava-native (Disposable already exists, but needs cooperative checks)
// Enhance internal checkpoints to check subscription disposal state" tabindex="0" role="button" style="box-sizing: border-box; margin: 8px !important; padding: 0px !important; font-size: 14px; font-weight: 500; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; appearance: none; border: 1px solid rgb(61, 68, 77); border-radius: 6px; line-height: 20px; display: inline-block; position: relative; color: rgb(240, 246, 252); background-color: rgb(33, 40, 48); box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color 80ms cubic-bezier(0.33, 1, 0.68, 1), box-shadow 80ms cubic-bezier(0.33, 1, 0.68, 1), border-color 80ms cubic-bezier(0.33, 1, 0.68, 1);">
Internal Checkpoints (minimum)
Following TypeScript's lead, cancellation should be checked at:
BaseLlmFlow.runOneStep()(already checksendInvocation, easy to extend)Functions.handleFunctionCalls()(currently has NO cancellation check)beforeModel/afterModel/beforeTool/afterToolSemantics (align with TypeScript
cancel.md)Questions for Maintainers
CancellationToken(explicit, testable) vs. leveraging RxJavaDisposablesemantics (less new API surface but less portable)?runLive(streaming/live mode), or start withrunAsynconly?temp:cancelledstate proposal), or just complete silently (like TypeScript)?References
/cancelAPI PR: feat: Add API endpoint to cancel in-progress agent tasks adk-python#5983I'm happy to contribute the implementation once the API direction is agreed upon. I have production experience implementing cooperative cancellation in a Java ADK-based agent platform (checkpoint-based, with checks at beforeModel/beforeTool/afterEvent boundaries).
SummaryJava ADK currently lacks a mechanism to cancel/abort a running agent invocation. TypeScript ADK already ships mature AbortSignal-based cancellation (docs/runtime/cancel.md), and Python ADK has active work in progress (google/adk-python#4825, google/adk-python#5983). Java ADK has no equivalent — no API, no internal checkpoints, and no graceful shutdown path.
Motivation
Cancellation is essential for production agent deployments:
User-facing "Stop" button — long-running multi-tool loops need immediate, graceful termination when the user clicks stop.
Timeout enforcement — server-side request deadlines (e.g., HTTP gateway timeouts) must propagate into the agent execution.
Resource cleanup — without cooperative cancellation, in-flight LLM streaming connections and tool executions leak resources.
Cost control — aborting early avoids unnecessary LLM token consumption.
Current State
Language Status Mechanism
TypeScript ✅ Shipped runner.runAsync(params, { signal }) with AbortSignal; checkpoints in LLM call, tool execution, and plugin callbacks; graceful generator completion (no throw)
Python 🚧 In progress google/adk-python#4825 (stop_event: asyncio.Event), google/adk-python#5983 (/cancel API + task.cancel())
Java ❌ Missing Only InvocationContext.endInvocation (internal flag, not externally triggerable); Disposable.dispose() is a hard RxJava cut (not graceful, tools don't check it); no RunConfig timeout/cancel field
Proposed Design (aligned with TypeScript)
API Surface
// Option A: CancellationToken (similar to .NET / structured concurrency)
CancellationTokenSource cts = new CancellationTokenSource();
RunConfig config = RunConfig.builder()
.cancellationToken(cts.token())
.build();
Flowable events = runner.runAsync(userId, sessionId, content, config);
// Later:
cts.cancel(); // triggers graceful shutdown
// Option B: RxJava-native (Disposable already exists, but needs cooperative checks)
// Enhance internal checkpoints to check subscription disposal state
Internal Checkpoints (minimum)
Following TypeScript's lead, cancellation should be checked at:
Before each LLM call — BaseLlmFlow.runOneStep() (already checks endInvocation, easy to extend)
Before each tool execution — Functions.handleFunctionCalls() (currently has NO cancellation check)
During LLM streaming — between chunks (for long generations)
Plugin/callback boundaries — beforeModel / afterModel / beforeTool / afterTool
Semantics (align with TypeScript cancel.md)
Graceful: the Flowable completes normally (no error signal), already-emitted events are preserved in the session.
Cooperative: cancellation is checked at defined points; in-flight atomic operations (single LLM call, single tool run) may complete before the next checkpoint.
Idempotent: calling cancel multiple times is safe.
Questions for Maintainers
API preference: CancellationToken (explicit, testable) vs. leveraging RxJava Disposable semantics (less new API surface but less portable)?
Scope: Should cancellation also cover runLive (streaming/live mode), or start with runAsync only?
Event emission on cancel: Should the framework emit a final "cancelled" event (like Python's temp:cancelled state proposal), or just complete silently (like TypeScript)?
References
TypeScript ADK cancel docs: https://google.github.io/adk-docs/runtime/cancel/
Python ADK cancellation PR: google/adk-python#4825
Python ADK /cancel API PR: google/adk-python#5983
Python ADK cancellation discussion: google/adk-python#4156
I'm happy to contribute the implementation once the API direction is agreed upon. I have production experience implementing cooperative cancellation in a Java ADK-based agent platform (checkpoint-based, with checks at beforeModel/beforeTool/afterEvent boundaries).