Frame Polling Fails With "URI is not absolute"
Metadata
- Date:
2026-04-03 - Status:
fixed - Severity:
high - Related issue/ticket:
N/A - Owner:
N/A
About
Overview: - During capture, frame polling logged frame_poll_error with java.lang.IllegalArgumentException: URI is not absolute. - This is important because frame discovery fails, so JPEG frames are never enqueued for upload and session visual data is effectively not saved to the backend.
Technical Questions: - Assumption validated: native startRecordingFrames()/stopRecordingFrames() return filesystem paths (for example /tmp/... or /data/...) without a URI scheme. - Bug age appears recent to current frame-polling implementation that switched to expo-file-system Directory. - Obvious miss: Directory/File were constructed with raw paths rather than absolute URIs. - Required state: SDK/device path (not stub mode), active frame recording, and polling/final-poll execution.
Resources: - Runtime error signal: main/app/lib/capture-session.ts (frame_poll_error, frame_final_poll_error). - Native return values: - main/app/wearables-module/android/src/main/java/expo/modules/wearablesmodule/WearablesModule.kt - main/app/wearables-module/ios/WearablesModule.swift - Fix and regression coverage: - main/app/lib/capture-session.ts - main/app/__tests__/capture-session.test.ts
Steps to cause failure
flowchart LR
StartCapture --> NativeReturnsPath["Native returns /tmp/... (no scheme)"]
NativeReturnsPath --> Poll["JS new Directory(path).list()"]
Poll --> Throws["URI is not absolute"]
Throws --> FramePollError["frame_poll_error logged"]
FramePollError --> NoUpload["No new frames enqueued/uploaded"] System
flowchart TD
Native[Wearables native module writes JPEGs to temp dir] --> JS[Capture session polling]
JS --> FS[expo-file-system Directory/File]
JS --> Queue[FrameUploadQueue]
Queue --> API[POST /sessions/:id/frames] Notes about the system can go here.
Reproduction Details
- Ensure capture runs with SDK available so native
startRecordingFrames()is used. - Return a bare absolute path (for example
/tmp/frames_123) from native frame-directory methods. - Polling attempts
new Directory(path).list()and throwsURI is not absolute; no frames are queued.
Reproduction test (unit preferred): npm test -- capture-session.test.ts --runInBand
Notes for PR
Root cause is path/URI contract mismatch between native frame directory outputs and expo-file-system URI expectations.
Fix normalizes all native/local paths to absolute URIs (file://...) before creating Directory and File objects in capture session code.
Regression test validates polling and final polling no longer hit URI-absolute errors for native path-style returns.
Audit Log
| ID | Action | Note | Context |
|---|---|---|---|
| 1 | Create audit log | Initialized investigation and dedupe check in docs/bugs | issue created |
| 2 | Inspect logs/code | Traced runtime error to capture-session frame polling and native path returns | root-cause analysis |
| 3 | Add regression test | Added test that enforces file:// URI normalization in polling/final polling path | reproduction harness |
| 4 | Fix root cause | Added toAbsoluteUri() normalization before Directory/File usage | code fix |
| 5 | Verify | Ran npm test -- capture-session.test.ts --runInBand and confirmed pass | post-fix validation |
| 6 | Code-review gate | Reviewed diff for scope, DRY/YAGNI, and test coverage of fix path | final check |
Verification
- [x] Reproduced failure before fix
- [x] Reproduction test fails before fix
- [x] Root cause identified with evidence
- [x] Fix applied at source (no workaround-only patch)
- [x] Reproduction test passes after fix
- [x] Reproduction path now passes
- [x] Regression test added/updated (or
N/Awith reason) - [x] Verified no duplicate solved-bug log exists for same root cause