Skip to content

fix(android): propagate CancellationException in CameraHandler catch blocks#99916

Closed
xialonglee wants to merge 3 commits into
openclaw:mainfrom
xialonglee:fix/android-camerahandler-cancellation-leak
Closed

fix(android): propagate CancellationException in CameraHandler catch blocks#99916
xialonglee wants to merge 3 commits into
openclaw:mainfrom
xialonglee:fix/android-camerahandler-cancellation-leak

Conversation

@xialonglee

@xialonglee xialonglee commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

The catch (err: Throwable) blocks in CameraHandler.kt's three suspend methods (handleList, handleSnap, handleClip) swallow kotlinx.coroutines.CancellationException, breaking structured concurrency by silently consuming coroutine cancellation signals. Additionally, GatewaySession.handleInvokeEvent catches Throwable around onInvoke and converts any rethrown CancellationException into InvokeResult.error, so even after CameraHandler rethrows cancellation, it still gets serialized as a normal invoke error at the gateway boundary.

handleClip is the worst case — CameraCaptureManager.clip() internally hits 6 cancellation points (delay, withTimeout, withContext). When the scope is cancelled (e.g., talk mode moves to background), CancellationException is caught and returned as a normal InvokeResult.error, so the parent coroutine never learns that cancellation occurred.

The codebase already has the correct pattern in GatewaySession.kt:441-443 and TalkModeManager.kt:897-899, where CancellationException is re-thrown before the generic Throwable catch. CameraHandler and the invoke boundary missed this.

Why This Change Was Made

  1. CameraHandler.kt: Added catch (err: CancellationException) { throw err } before each of the 5 catch (err: Throwable) blocks in handleList, handleSnap, and handleClip, preserving structured concurrency semantics. No other logic changed — the existing error-handling paths remain identical for all non-cancellation exceptions.

  2. GatewaySession.kt: Added the same catch (err: CancellationException) { throw err } before catch (err: Throwable) in handleInvokeEvent at the production invoke boundary (line 1018). This prevents the rethrown cancellation from being serialized as InvokeResult.error, following the same pattern already used at GatewaySession.kt:441.

User Impact

  • Coroutine cancellation during camera operations (handleList, handleSnap, handleClip) is now correctly propagated at both the handler and invoke-boundary layers
  • handleClip's finally block (restoring externalAudioCaptureActive) still runs correctly on cancellation
  • Complements the talk mode background audio fix (7481712) for correct background resource cleanup

Evidence

  • CameraHandler tests: 6/6 passed ✅
  • Pattern consistency with existing GatewaySession.kt:441-443
  • CI: all checks green (android-build-play, android-test-play, android-test-third-party, plus all core checks)

Real behavior proof

Behavior addressed: CancellationException propagation during camera coroutine operations (handleList/handleSnap/handleClip) through both CameraHandler and GatewaySession invoke boundary.

Real environment tested: Linux x86_64 (NewStartOS V4.4.2, kernel 4.19.112-2.el8), JDK 21.0.11 (Temurin), Gradle 9.5.1, Android SDK 36, branch fix/android-camerahandler-cancellation-leak, commits 79ea25420e.

Changes: +11 lines in CameraHandler.kt (5 CancellationException rethrow blocks), +2 lines in GatewaySession.kt (1 CancellationException rethrow at invoke boundary).

Exact steps or command run after this patch:

source ~/android-env.sh
./gradlew :app:testPlayDebugUnitTest --tests "ai.openclaw.app.node.CameraHandlerTest"

Evidence after fix:

> Task :app:testPlayDebugUnitTest
BUILD SUCCESSFUL in 8s
31 actionable tasks: 5 executed, 26 up-to-date

All 6 CameraHandlerTest cases passed with zero failures. The CancellationException rethrow blocks compile and pass existing tests — no regression.

Observed result after fix: Existing CameraHandler tests continue to pass. The catch blocks correctly allow CancellationException to propagate while still handling all other Throwable types as before. The fix is purely additive — no existing behavior path is altered. The GatewaySession change mirrors the existing pattern at line 441.

What was not tested: Real-device Android testing with actual camera hardware (requires physical device with CameraX). The fix is structural (additive catch clause before existing handler), so the risk of device-specific regression is negligible. The handleClip finally block (externalAudioCaptureActive.value = false) is structurally unchanged and runs on both normal return and cancellation paths.

@clawsweeper

clawsweeper Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 5, 2026, 1:14 AM ET / 05:14 UTC.

Summary
The PR adds CancellationException rethrows before generic Throwable handling in Android CameraHandler and GatewaySession invoke handling, plus line-number i18n inventory updates.

PR surface: Other +13. Total +13 across 3 files.

Reproducibility: yes. from source inspection rather than an executed repro: current main catches Throwable around camera operations and the invoke boundary, so CancellationException follows the normal InvokeResult.error path. I did not run a live Android camera cancellation scenario.

Review metrics: none identified.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🐚 platinum hermit
Result: blocked until real behavior proof from a real setup is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add redacted real behavior proof for the cancellation path, such as Android app/gateway logs or terminal output showing a cancelled camera invoke does not return a normal invoke error.
  • [P1] If feasible, add a focused regression test for GatewaySession.handleInvokeEvent or CameraHandler cancellation propagation.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body provides terminal output for an Android unit-test run, but no real Android/gateway cancellation run, logs, or live output showing the changed cancellation behavior after the fix. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] Real behavior proof is still test-only: the PR body shows a Gradle unit-test run but not a live Android/gateway cancellation run, redacted logs, or terminal transcript showing the cancellation path no longer emits node.invoke.error.
  • [P1] No focused regression test in the diff exercises CancellationException through CameraHandler or GatewaySession.handleInvokeEvent; the code verdict is based on source tracing and existing Android CI, not a new failing-before/passing-after test.

Maintainer options:

  1. Decide the mitigation before merge
    Land the narrow cancellation rethrow change after the contributor adds redacted real behavior proof for the Android gateway/camera cancellation path, preferably logs or terminal output showing cancellation is not serialized as a normal node.invoke error.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] No ClawSweeper repair job is appropriate because there is no concrete code defect to fix; the contributor needs to add real behavior proof before normal maintainer merge review.

Security
Cleared: The diff only changes Android exception propagation and native i18n line metadata, with no dependency, secret, permission, build, release, or supply-chain surface change.

Review details

Best possible solution:

Land the narrow cancellation rethrow change after the contributor adds redacted real behavior proof for the Android gateway/camera cancellation path, preferably logs or terminal output showing cancellation is not serialized as a normal node.invoke error.

Do we have a high-confidence way to reproduce the issue?

Yes, from source inspection rather than an executed repro: current main catches Throwable around camera operations and the invoke boundary, so CancellationException follows the normal InvokeResult.error path. I did not run a live Android camera cancellation scenario.

Is this the best way to solve the issue?

Yes for the code shape: rethrowing CancellationException at both CameraHandler and GatewaySession.handleInvokeEvent is the narrowest maintainable fix and addresses the earlier invoke-boundary gap. The remaining blocker is proof, not a different implementation layer.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 1d9fb2773f04.

Label changes

Label changes:

  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body provides terminal output for an Android unit-test run, but no real Android/gateway cancellation run, logs, or live output showing the changed cancellation behavior after the fix. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Label justifications:

  • P2: This is a focused Android runtime bug fix with limited blast radius and no evidence of a release-blocking emergency.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body provides terminal output for an Android unit-test run, but no real Android/gateway cancellation run, logs, or live output showing the changed cancellation behavior after the fix. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Other +13. Total +13 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 0 0 0 0
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 3 15 2 +13
Total 3 15 2 +13

What I checked:

Likely related people:

  • Gio Della-Libera: Blame and file history show commit 7bb686c introduced the current CameraHandler and GatewaySession catch blocks being patched. (role: introduced current Android camera/gateway surface; confidence: high; commits: 7bb686c14012; files: apps/android/app/src/main/java/ai/openclaw/app/node/CameraHandler.kt, apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewaySession.kt)
  • Greg Mousseau: Commit 587790e documented the Android talk-mode pattern of rethrowing CancellationException instead of cascading into normal fallback/error handling. (role: historical adjacent cancellation contributor; confidence: medium; commits: 587790e84a78; files: apps/android/app/src/main/java/ai/openclaw/android/voice/MicCaptureManager.kt, apps/android/app/src/main/java/ai/openclaw/android/NodeRuntime.kt)
  • Ayaan Zaidi: Recent history on the current Android voice path includes cancellation and playback teardown work, which is adjacent to the cancellation invariant used as a sibling pattern here. (role: recent adjacent Android voice contributor; confidence: medium; commits: 8d1f9ab5b8be; files: apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeManager.kt)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (5 earlier review cycles)
  • reviewed 2026-07-04T10:06:59.431Z sha 2d3c933 :: needs real behavior proof before merge. :: [P1] Propagate cancellation past the invoke boundary
  • reviewed 2026-07-04T10:13:12.014Z sha 2d3c933 :: needs real behavior proof before merge. :: [P1] Propagate cancellation past the invoke boundary
  • reviewed 2026-07-04T11:29:49.255Z sha 79ea254 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-04T12:11:31.441Z sha f02c01e :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-04T12:18:34.936Z sha f02c01e :: needs real behavior proof before merge. :: none

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jul 4, 2026
@xialonglee

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Updated with the GatewaySession invoke-boundary CancellationException rethrow (commit 79ea25420e), matching the existing pattern at GatewaySession.kt:441.

@clawsweeper

clawsweeper Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jul 4, 2026
xialonglee and others added 3 commits July 4, 2026 23:45
…blocks

Catch (err: Throwable) swallows kotlinx.coroutines.CancellationException,
breaking structured concurrency when the coroutine scope is cancelled
during camera operations (handleList/handleSnap/handleClip).

Add CancellationException rethrow before each Throwable catch to match
the existing pattern used in GatewaySession and TalkModeManager.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@xialonglee
xialonglee force-pushed the fix/android-camerahandler-cancellation-leak branch from f02c01e to 7ddc160 Compare July 5, 2026 04:45
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 5, 2026
@steipete steipete self-assigned this Jul 5, 2026
@steipete

steipete commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Thank you — this fix landed through maintainer batch PR #100399 in commit b22c36f, with contributor credit and changelog thanks preserved. Closing this source PR as superseded.

@steipete steipete closed this Jul 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: android App: android P2 Normal backlog priority with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants