You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Due for payment 2026-03-30] Workspace settings - Photo is not updated on WS #85673
👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:
Identify the pull request that introduced this issue and revert it.
Classification: Frontend bug Causing PR: #85248 - "Bump onyx to 3.0.46" by @VickyStash (High confidence) Related Issues: #76628 (similar previous issue with avatar updates)
Recommendation: REVERT
The PR should be reverted to restore avatar upload functionality. A roll-forward fix would require investigating the interaction between the new synchronous Onyx subscriber behavior and the avatar upload flow.
Labels: Removing DeployBlocker (no backend changes involved)
Detailed Analysis
Evidence
PR #85248 bumped react-native-onyx from 3.0.45 to 3.0.46, which changed subscriber notifications to be more synchronous (removing macro/microtasks). This PR is in the staging deploy (v9.3.40-0) but not in production, matching the reproduction pattern (staging: yes, production: no).
The PR description explicitly states:
"This Onyx update changes subscriber notifications to be more synchronous, which exposed two issues in the App"
While the PR fixed two known issues (side effect in withPolicy HOC and new expense scroll/highlight race condition), the more synchronous behavior likely exposed a new timing issue with workspace avatar uploads.
Verification
File affected: src/pages/workspace/WorkspaceOverviewPage.tsx uses AvatarWithImagePicker with onImageSelected={(file) => updateWorkspaceAvatar(policyID, policy.avatarURL, file as File)}
Upload flow: When user clicks "Save" after cropping, AvatarCropModal calls cropAndSaveImage → onSave(newImage) → updateWorkspaceAvatar → API.write with optimistic data
Optimistic update: Should immediately set policy.avatarURL to file.uri, but something is preventing this from working correctly
PR Bump onyx to 3.0.46 #85248 is in staging deploy: Listed at position 36 in the chronologically ordered PR list in the Deploy Checklist
The more synchronous Onyx subscriber behavior in 3.0.46 appears to create a race condition or timing issue that prevents the optimistic avatarURL update from persisting when uploading workspace avatars. The exact mechanism likely involves:
The optimistic data sets policy.avatarURL to the cropped image file.uri
The synchronous Onyx notifications cause re-renders or policy object updates that either:
Overwrite the optimistic avatar URL before it can be displayed
Prevent the avatar component from re-rendering with the new URL
Interfere with the pending action state that should protect the optimistic update
The fix to withPolicy.tsx (moving updateLastAccessedWorkspace into useEffect) was necessary to prevent "Cannot update a component while rendering" errors, but the broader synchronous behavior change exposed this avatar upload regression.
Please re-state the problem that we are trying to solve in this issue.
When a user navigates to Workspace Settings and tries to upload a new workspace avatar image (click avatar → upload image → click "Save"), the image is not updated. This is reproducible on staging (v9.3.40-0) but not on production, indicating a regression in the current release.
What is the root cause of that problem?
The most likely root cause is PR #85231 ("Fix - heic context lost"), which migrated src/libs/cropOrRotateImage/index.ts (the web platform version) from the deprecated manipulateAsync API to the new class-based ImageManipulator.manipulate API from expo-image-manipulator.
The workspace avatar upload flow is:
AvatarWithImagePicker opens AttachmentPicker → user selects image
validateAvatarImage validates the file (extension, size, corruption, resolution)
AvatarCropModal opens → user crops → clicks Save
cropOrRotateImage processes the image (crop/rotate via canvas) → creates a File object
Step 4 is where PR #85231 changed the implementation. The old code used the deprecated manipulateAsync(uri, actions, options) function. The new code uses ImageManipulator.manipulate(uri) with chained .crop()/.rotate() calls, then .renderAsync() → .saveAsync().
While both approaches ultimately go through the same expo module (NativeImageManipulatorModule.web.ts → loadImageAsync → canvas operations → blob), the migration introduces a new code path. The old manipulateAsync wrapper internally called context.release() and image.release() after saving, while the new code omits these cleanup calls. More importantly, the new code handles errors differently — if renderAsync() or saveAsync() fails, the promise rejects, and AvatarCropModal catches it at line 329 by only resetting isLoading to false without showing any error message. This means any failure in the image manipulation chain would silently fail.
A secondary contributing factor could be PR #70740 ("Improve and simplify attachment and file validation"), which extensively refactored FileUtils.ts (including validateImageForCorruption used by the avatar validation path) and is already confirmed as the cause of deploy blocker #85671 ("No content to add" error when uploading attachments).
Key evidence:
src/libs/cropOrRotateImage/index.ts:1-32 — The migrated web implementation
src/components/AvatarCropModal/AvatarCropModal.tsx:321-331 — The crop/save flow with silent error handling
src/pages/workspace/WorkspaceOverviewPage.tsx:583-588 — The onImageSelected callback calling updateWorkspaceAvatar
src/libs/actions/Policy/Policy.ts:1688-1731 — The updateWorkspaceAvatar action
What changes do you think we should make in order to solve the problem?
Option A (Quick fix — revert the web migration): Revert src/libs/cropOrRotateImage/index.ts back to using the deprecated manipulateAsync API. This is the safest fix for unblocking the deploy since the old code was working in production. PR #85231's HEIC fix was primarily for native platforms, and the web version change may not have been necessary.
Option B (Fix forward): Add proper error logging and debugging to cropOrRotateImage/index.ts to identify the exact failure point in the new API chain (manipulate → renderAsync → saveAsync → fetch). Also improve AvatarCropModal's error handling (line 329) to display an error message to the user instead of silently resetting.
I recommend Option A for the deploy blocker fix, with a follow-up investigation for Option B.
What alternative solutions did you explore? (Optional)
Investigated whether useAvatarMenu hook changes caused the issue — confirmed it is only used by profile avatar, NOT workspace avatar
Checked the expo-image-manipulator web implementation (NativeImageManipulatorModule.web.ts, ImageManipulatorContext.web.ts, ImageManipulatorImageRef.web.ts) — the underlying canvas-based image processing appears correct, but runtime behavior may differ from what the static code suggests
Next Steps for Contributor+ team: Reply with @MelvinBot implement this to create a draft PR, @MelvinBot to refine this analysis, or explain why you are rejecting Melvin's proposal.
changed the title [-]Workspace settings - Photo is not updated on WS[/-][+][Due for payment 2026-03-30] Workspace settings - Photo is not updated on WS[/+]on Mar 23, 2026
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.3.41-4 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:
@Julesssss The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]
Activity
melvin-bot commentedon Mar 18, 2026
Triggered auto assignment to @Julesssss (
DeployBlockerCash), see https://stackoverflowteams.com/c/expensify/questions/9980/ for more details.melvin-bot commentedon Mar 18, 2026
💬 A slack conversation has been started in #expensify-open-source
github-actions commentedon Mar 18, 2026
👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:
OSBotify commentedon Mar 18, 2026
Investigation Summary
Classification: Frontend bug
Causing PR: #85248 - "Bump onyx to 3.0.46" by @VickyStash (High confidence)
Related Issues: #76628 (similar previous issue with avatar updates)
Recommendation: REVERT
The PR should be reverted to restore avatar upload functionality. A roll-forward fix would require investigating the interaction between the new synchronous Onyx subscriber behavior and the avatar upload flow.
Labels: Removing
DeployBlocker(no backend changes involved)Detailed Analysis
Evidence
PR #85248 bumped
react-native-onyxfrom 3.0.45 to 3.0.46, which changed subscriber notifications to be more synchronous (removing macro/microtasks). This PR is in the staging deploy (v9.3.40-0) but not in production, matching the reproduction pattern (staging: yes, production: no).The PR description explicitly states:
While the PR fixed two known issues (side effect in withPolicy HOC and new expense scroll/highlight race condition), the more synchronous behavior likely exposed a new timing issue with workspace avatar uploads.
Verification
src/pages/workspace/WorkspaceOverviewPage.tsxusesAvatarWithImagePickerwithonImageSelected={(file) => updateWorkspaceAvatar(policyID, policy.avatarURL, file as File)}AvatarCropModalcallscropAndSaveImage→onSave(newImage)→updateWorkspaceAvatar→API.writewith optimistic datapolicy.avatarURLtofile.uri, but something is preventing this from working correctlyRoot Cause
The more synchronous Onyx subscriber behavior in 3.0.46 appears to create a race condition or timing issue that prevents the optimistic
avatarURLupdate from persisting when uploading workspace avatars. The exact mechanism likely involves:policy.avatarURLto the cropped imagefile.uriThe fix to
withPolicy.tsx(movingupdateLastAccessedWorkspaceintouseEffect) was necessary to prevent "Cannot update a component while rendering" errors, but the broader synchronous behavior change exposed this avatar upload regression.MelvinBot commentedon Mar 18, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
When a user navigates to Workspace Settings and tries to upload a new workspace avatar image (click avatar → upload image → click "Save"), the image is not updated. This is reproducible on staging (v9.3.40-0) but not on production, indicating a regression in the current release.
What is the root cause of that problem?
The most likely root cause is PR #85231 ("Fix - heic context lost"), which migrated
src/libs/cropOrRotateImage/index.ts(the web platform version) from the deprecatedmanipulateAsyncAPI to the new class-basedImageManipulator.manipulateAPI fromexpo-image-manipulator.The workspace avatar upload flow is:
AvatarWithImagePickeropensAttachmentPicker→ user selects imagevalidateAvatarImagevalidates the file (extension, size, corruption, resolution)AvatarCropModalopens → user crops → clicks SavecropOrRotateImageprocesses the image (crop/rotate via canvas) → creates aFileobjectWorkspaceOverviewPage.onImageSelectedcallsupdateWorkspaceAvatar(policyID, avatarURL, file)Step 4 is where PR #85231 changed the implementation. The old code used the deprecated
manipulateAsync(uri, actions, options)function. The new code usesImageManipulator.manipulate(uri)with chained.crop()/.rotate()calls, then.renderAsync()→.saveAsync().While both approaches ultimately go through the same expo module (
NativeImageManipulatorModule.web.ts→loadImageAsync→ canvas operations → blob), the migration introduces a new code path. The oldmanipulateAsyncwrapper internally calledcontext.release()andimage.release()after saving, while the new code omits these cleanup calls. More importantly, the new code handles errors differently — ifrenderAsync()orsaveAsync()fails, the promise rejects, andAvatarCropModalcatches it at line 329 by only resettingisLoadingtofalsewithout showing any error message. This means any failure in the image manipulation chain would silently fail.A secondary contributing factor could be PR #70740 ("Improve and simplify attachment and file validation"), which extensively refactored
FileUtils.ts(includingvalidateImageForCorruptionused by the avatar validation path) and is already confirmed as the cause of deploy blocker #85671 ("No content to add" error when uploading attachments).Key evidence:
src/libs/cropOrRotateImage/index.ts:1-32— The migrated web implementationsrc/components/AvatarCropModal/AvatarCropModal.tsx:321-331— The crop/save flow with silent error handlingsrc/pages/workspace/WorkspaceOverviewPage.tsx:583-588— TheonImageSelectedcallback callingupdateWorkspaceAvatarsrc/libs/actions/Policy/Policy.ts:1688-1731— TheupdateWorkspaceAvataractionWhat changes do you think we should make in order to solve the problem?
Option A (Quick fix — revert the web migration): Revert
src/libs/cropOrRotateImage/index.tsback to using the deprecatedmanipulateAsyncAPI. This is the safest fix for unblocking the deploy since the old code was working in production. PR #85231's HEIC fix was primarily for native platforms, and the web version change may not have been necessary.Option B (Fix forward): Add proper error logging and debugging to
cropOrRotateImage/index.tsto identify the exact failure point in the new API chain (manipulate→renderAsync→saveAsync→fetch). Also improveAvatarCropModal's error handling (line 329) to display an error message to the user instead of silently resetting.I recommend Option A for the deploy blocker fix, with a follow-up investigation for Option B.
What alternative solutions did you explore? (Optional)
useAvatarMenuhook changes caused the issue — confirmed it is only used by profile avatar, NOT workspace avatarvalidateAvatarImagefromAvatarUtils.tsrather than the refactoredvalidateAttachmentFile.ts, making it less likely to be the direct cause of THIS issueNativeImageManipulatorModule.web.ts,ImageManipulatorContext.web.ts,ImageManipulatorImageRef.web.ts) — the underlying canvas-based image processing appears correct, but runtime behavior may differ from what the static code suggestsNext Steps for Contributor+ team: Reply with @MelvinBot implement this to create a draft PR, @MelvinBot to refine this analysis, or explain why you are rejecting Melvin's proposal.
VickyStash commentedon Mar 18, 2026
I've just checked this issue over ad-hoc build from Onyx to 3.0.46 bump PR and wasn't able to reproduce it there. So I guess it's not related.
IuliiaHerets commentedon Mar 19, 2026
Chrome / v9.3.40-4 PR:85752
Reproduced
Screen.Recording.2026-03-19.at.12.22.23.mp4
VickyStash commentedon Mar 19, 2026
@IuliiaHerets I think you posted a comment in the wrong issue
IuliiaHerets commentedon Mar 19, 2026
@VickyStash I added the wrong video, fixed it already
3 remaining items
m-natarajan commentedon Mar 19, 2026
Passed, Chrome / v9.3.40-6
Screen.Recording.2026-03-19.at.15.26.09.mp4
cristipaval commentedon Mar 19, 2026
Thanks! Closing it then
[-]Workspace settings - Photo is not updated on WS[/-][+][Due for payment 2026-03-30] Workspace settings - Photo is not updated on WS[/+]melvin-bot commentedon Mar 23, 2026
Reviewinglabel has been removed, please complete the "BugZero Checklist".melvin-bot commentedon Mar 23, 2026
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.3.41-4 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:
If no regressions arise, payment will be issued on 2026-03-30. 🎊
melvin-bot commentedon Mar 23, 2026
@Julesssss The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]
melvin-bot commentedon Mar 23, 2026
Triggered auto assignment to @mallenexpensify (
Awaiting Payment)