Skip to content

[runtime][java][python] Preserve internal subagent framework faults through tool calls and the Python bridge - #1344

Open
zzwqqq wants to merge 2 commits into
apache:mainfrom
zzwqqq:issue-1322-framework-faults
Open

zzwqqq wants to merge 2 commits into
apache:mainfrom
zzwqqq:issue-1322-framework-faults

Conversation

@zzwqqq

@zzwqqq zzwqqq commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Linked issue: #1322 (second item only)

Purpose of change

Preserve framework faults from internal subagent calls, including their original causes. These faults currently become failed tool responses and can then be masked by an unresolved-handle error.

This PR addresses the second item only. Group cleanup, ordinary future/gather behavior, and model routing/retry policies are unchanged.

Runtime flow

Internal call operations and ActionState writes mark framework faults with UncapturableException. FunctionTool, ToolCallAction, subgraph execution, and durable recording propagate that marker through wrapped causes. The Python bridge carries the Java Throwable or Python error details back to Java before result conversion.

Key decisions

UncapturableException moves to plan.exceptions so plan and runtime share the classification. Python uses an internal counterpart and a per-call return envelope because Pemja otherwise loses the type and cause.

Behavioral Semantics

Interaction decisions

Failure / path Result
Ordinary child failure, Java or Python Failed SubagentResult
Marked tool/subagent fault, direct or nested, serial or parallel dispatch Propagates with its cause
Wait failure, even if the child also failed Propagates the wait failure
Ordinary / marked tool metadata failure Existing fallback / propagate the fault
Marked durable call or completion fault No terminal failure recorded; earlier completed work remains available
Java-origin / Python-origin fault crossing the bridge Original Java Throwable / Python type, message and traceback

Behavioral contracts

Nested calls retain the framework cause. Recovery can reuse completed child work and retry unfinished work after the storage fault is removed. Ordinary tool failures keep their existing handling, and user return values cannot be mistaken for bridge faults.

Failure behavior

Marked faults fail the Action through the existing runtime path; no retry policy is added. Cancellation, interruption and fatal errors retain their handling. Invalid bridge envelopes raise framework faults. Interpreter close still runs if dispatcher cleanup fails, retaining the first failure and suppressing a later close failure. User catch blocks can still handle exceptions.

Tests

Behavior Coverage
Framework faults escape tool-response conversion FunctionToolTest, ToolCallActionSubagentTest; Python test_tool_call_action.py and test_subagent_await_cleanup.py
Nested calls retain the cause; ordinary child failures remain results InternalSubagentFrameworkFaultTest, FrameworkFaultBridgeTest
Wait failures remain distinct from child failures SubgraphInvocationTest
Framework faults are not recorded as terminal failures; recovery reuses completed work Java/Python durable tests and the operator snapshot/restore case
The Python bridge preserves causes, diagnostics and ordinary return values FrameworkFaultBridgeTest
Invalid envelopes fail; interpreter close survives cleanup errors PythonFaultBridgeTest

Not verified: distributed-cluster fault injection, external model-service E2E, or Python 3.10/3.12. Python-child operator tests require JDK 21; the existing JDK 17 restriction is unchanged.

Validation details
Local check (Flink 2.3.0) Result
Java UT, JDK 21 3,741 tests, 129 skipped, no failures/errors
Python UT, Python 3.11 2,928 passed, 16 skipped, 201 deselected
Native Pemja/MiniCluster tests, JDK 21, no external model service 11 passed
New Java core regressions, JDK 17 19 passed
Spotless, Ruff, Apache RAT, git diff --check Passed

Java: env -u OPENAI_API_KEY ./tools/ut.sh -j, with live OpenAI tests skipped. Python: pytest with not e2e_tests and not integration, matching the tools/ut.sh -p selection.

Classification uses explicit causes. The bridge envelope belongs to one call and never enters the journal; ordinary Python exceptions retain Pemja's exception path. Attribute lookup and close-time cleanup manage the dispatcher's references.

API

No user-facing API signatures or YAML options change. The internal UncapturableException moves from runtime.operator.parallel to plan.exceptions; code importing it must update imports and recompile.

Documentation

  • doc-needed
  • doc-not-needed
  • doc-included

Was this patch authored or co-authored using generative AI tooling?

  • Yes
  • No

Generated-by: Codex 0.164 (gpt-6)

…hrough tool calls and the Python bridge

Generated-by: Codex 0.164 (gpt-6)
@github-actions github-actions Bot added doc-included Your PR already contains the necessary documentation updates. fixVersion/1.0.0 The feature or bug should be implemented/fixed in the 1.0.0 version. priority/major Default priority of the PR or issue. and removed doc-included Your PR already contains the necessary documentation updates. labels Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

doc-included Your PR already contains the necessary documentation updates. fixVersion/1.0.0 The feature or bug should be implemented/fixed in the 1.0.0 version. priority/major Default priority of the PR or issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant