margelo / margelo/react-native-vision-camera
Android: setTorchMode promise is fire-and-forget; 'off' on a flash-less camera (front camera) rejects with 'No flash unit' as an unhandled rejection on every mount
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 9.6k
- Forks
- 1.4k
- Avg merge
- 1d 28m
- Merged PRs (30d)
- 4
Description
What's happening?
Setting the torchMode prop to any value — including 'off' — on a camera whose bound camera has no flash unit produces an unhandled promise rejection on every mount/update:
Error: java.lang.IllegalStateException: No flash unit
at androidx.camera.camera2.impl.TorchControl.setTorchAsync-Oup_wC0$camera_camera2(TorchControl.kt:128)
at androidx.camera.camera2.adapter.CameraControlAdapter.enableTorch(CameraControlAdapter.kt:115)
at androidx.camera.core.impl.AdapterCameraControl.enableTorch(AdapterCameraControl.java:73)
at com.margelo.nitro.camera.hybrids.HybridCameraController$setTorchMode$1.invokeSuspend(HybridCameraController.kt:205)
...
mechanism: onunhandledrejection
Two compounding causes in react-native-vision-camera@5.1.0:
useTorchModeUpdaternever handles rejection (src/hooks/internal/useTorchModeUpdater.ts):
useEffect(() => {
if (controller == null) return
if (torchMode == null) return
controller.setTorchMode(torchMode) // ← returned Promise is neither awaited nor .catch()-ed
}, [controller, torchMode])
Any native failure becomes a global unhandled rejection the app cannot intercept (there is no onError route for it, and no imperative handle to try/catch).
setTorchModecallsenableTorchunconditionally (android/.../HybridCameraController.kt:200): CameraX'sTorchControlfails the returned future withIllegalStateException("No flash unit")for anyenableTorch(...)call on a camera without a flash unit — evenenableTorch(false). There is nohasFlashUnitpre-check.
The failure mode is easy to hit accidentally: an app that renders torchMode={torchOn ? 'on' : 'off'} (a natural way to write a declarative prop) emits one Sentry-reported unhandled rejection per mount of any flash-less camera — the front camera included. In our production app (Sentry ID ALLY-HOME-NATIVE-22) this produced 4,438 events across 190 users in 5 days, almost all from front-camera screens where the torch was never touched.
It can also occur with a correctly-guarded prop: on some logical multi-camera devices (observed on a Galaxy Z Fold) device.hasTorch is true while the bound physical camera has no flash unit, so even torchMode='on' behind a hasTorch check rejects.
Reproduction
<Camera device={frontDevice} isActive torchMode="off" ... />(or any device whose bound camera lacks a flash unit) on Android.- Observe an unhandled promise rejection
java.lang.IllegalStateException: No flash uniton mount — no torch interaction needed.
Suggested fix
Either (ideally both):
- In
useTorchModeUpdater, handle the promise:controller.setTorchMode(torchMode).catch(...)— route to the cameraonErrorhandler or log, so library-internal failures can't become app-level unhandled rejections. (Same pattern applies to the other fire-and-forget updaters.) - In the Android
setTorchMode, no-op (or reject with a typed, documented error) when the camera has no flash unit —camera.cameraInfo.hasFlashUnit()— at minimum for theOFFcase, which is always semantically a no-op on a flash-less camera.
Workaround
Pass torchMode={hasTorch ? (torchOn ? 'on' : 'off') : undefined} — useTorchModeUpdater skips null/undefined, so the native call never happens. This doesn't cover the hasTorch-mismatch case on logical multi-cameras.
Environment
- react-native-vision-camera 5.1.0 (Nitro), react-native 0.86.0, Expo SDK 57, New Architecture
- Android (CameraX path); observed on Samsung SM-A156U (Galaxy A15), Galaxy Z Fold, and other devices
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/hooks/internal/useTorchModeUpdater.ts and android/.../HybridCameraController.kt around setTorchMode, then reproduce with a flash-less front camera and torchMode="off". Done means the native no-flash case no longer causes an unhandled rejection and torch updates still behave correctly on cameras with a flash unit.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, kotlin, react-native, typescript
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100