Skip to content

fix: Catch routine CameraX cancellation rejections in internal updater hooks#4093

Open
ssahil97 wants to merge 1 commit into
mrousavy:mainfrom
ssahil97:fix/hook-updater-unhandled-cancellation-rejections
Open

fix: Catch routine CameraX cancellation rejections in internal updater hooks#4093
ssahil97 wants to merge 1 commit into
mrousavy:mainfrom
ssahil97:fix/hook-updater-unhandled-cancellation-rejections

Conversation

@ssahil97

Copy link
Copy Markdown

What

The internal prop-sync hooks fire CameraController calls without awaiting or catching them:

  • useZoomUpdatercontroller.setZoom(..)
  • useTorchModeUpdatercontroller.setTorchMode(..)
  • useExposureUpdatercontroller.setExposureBias(..)
  • useCameraControllerConfiguration → uncaught load() around controller.configure(..)

On Android, CameraX rejects a pending control operation with OperationCanceledException when a newer value supersedes an in-flight one ("Cancelled due to another zoom value being set") or when the camera is not currently active ("Camera is not active"). Because the hooks fire-and-forget, every such routine cancellation surfaces as an unhandled promise rejection in the app — which error reporters (Sentry etc.) capture as an error flood.

This PR adds a small shared helper, ignoreCameraCancellation(promise), and routes all four call sites through it. Routine cancellations are swallowed; any other error is re-thrown inside the catch, so non-cancellation failures keep exactly their previous unhandled-rejection visibility — nothing new gets silently hidden.

Why #3901 doesn't fully cover this

#3909 was closed pointing to #3901 (awaitable start()/stop(), merged pre-5.1.0). That fix helps the session lifecycle, but the JS hooks still fire the controller calls unawaited, and "Camera is not active" / supersede cancellations still legitimately occur (e.g. a torch prop write while the session isn't running yet, or rapid pinch-zoom superseding in-flight zoom values).

Production evidence on 5.1.0 (which includes #3901): in our app (React Native 0.83, new architecture, Android 15 devices incl. Samsung S23 and Lenovo TB352XU) we see continuous Sentry unhandled-rejection events from both ZoomControl.applyZoomState ("Cancelled due to another zoom value being set" / "Camera is not active") and TorchControl.setTorchAsync ("Camera is not active" — we only ever set torch="off"). The lib/hooks/internal code in 5.1.1 is unchanged, so the leak persists on latest.

Closes #3907 (torch variant of #3909).

Notes

  • The worklet-side path (VisionCameraWorkletsProxy.bindUIUpdatesToController) is untouched — this PR only covers the JS effect call sites.
  • Message-string matching is limited to the Android CameraX cancellation texts; iOS rejections don't match and keep their current behavior.
  • We've been running the zoom+torch variant of this change as a pnpm patch in production and it eliminates the rejection flood without masking real errors.

🤖 Generated with Claude Code

https://claude.ai/code/session_01846JqK8mH4c3BrQyksjwbz

@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

@ssahil97 is attempting to deploy a commit to the Margelo Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Android: setTorchMode leaks OperationCanceledException ("Camera is not active") as Uncaught Promise

1 participant