Fix: cannot load segmentation after export in local mode#5999
Fix: cannot load segmentation after export in local mode#5999nithin-trenser wants to merge 3 commits into
Conversation
❌ Deploy Preview for ohif-dev failed. Why did it fail? →
|
| const combined = frameNumber && combineFrameInstance(frameNumber, instance); | ||
| if (combined) { | ||
| // Add imageId to multiframe result so it matches single-frame instance. | ||
| combined.imageId = imageId; | ||
| return combined; | ||
| } |
There was a problem hiding this comment.
Mutation of a cached combined instance
createCombinedValue inside combineFrameInstance caches the per-frame object at instance._parentInstance[frameNumber] and returns the same reference on every subsequent call. Setting combined.imageId = imageId permanently stamps that imageId onto the cached object. If the same SOP-instance frame is ever resolved through two different imageIds (e.g., the same DICOM served from two WADO-RS endpoints), the second caller receives a combined instance whose imageId belongs to the first caller.
Additionally, when NumberOfFrames < 2 combineFrameInstance returns the original instance reference unchanged. If frameNumber is somehow truthy for such an instance, combined === instance, and combined.imageId = imageId mutates the shared object stored in DicomMetadataStore directly — not just its per-frame projection.
Prompt To Fix With AI
This is a comment left during a code review.
Path: platform/core/src/classes/MetadataProvider.ts
Line: 63-68
Comment:
**Mutation of a cached combined instance**
`createCombinedValue` inside `combineFrameInstance` caches the per-frame object at `instance._parentInstance[frameNumber]` and returns the same reference on every subsequent call. Setting `combined.imageId = imageId` permanently stamps that `imageId` onto the cached object. If the same SOP-instance frame is ever resolved through two different imageIds (e.g., the same DICOM served from two WADO-RS endpoints), the second caller receives a combined instance whose `imageId` belongs to the first caller.
Additionally, when `NumberOfFrames < 2` `combineFrameInstance` returns the original `instance` reference unchanged. If `frameNumber` is somehow truthy for such an instance, `combined === instance`, and `combined.imageId = imageId` mutates the shared object stored in `DicomMetadataStore` directly — not just its per-frame projection.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Fixed by avoiding mutation of cached/shared metadata objects in combineFrameInstance.
Previously, combined.imageId = imageId modified shared references returned from createCombinedValue, which could leak imageId values across callers. For single-frame instances, it could also mutate the original metadata object stored in DicomMetadataStore.
Updated the implementation to return a cloned object before assigning imageId.
Context
Fix issue : #5540 and related fix in segmentation load for multiframe data : cornerstonejs/cornerstone3D#2727
Segmentation is not loading after export in /local mode.
Fixed jump to segment feature for multiframe data.
Changes & Results
OHIFCornerstoneSEGViewport._getReferencedDisplaySetMetadata.Cannot destructure property 'SpacingBetweenSlices' of 'a' as it is undefined.SharedFunctionalGroupsSequence.PixelMeasuresSequencecan be an empty array ([]). The current code assumes it has an item and does:PixelMeasures = PixelMeasuresSequence[0]-> undefined then destructures{ SpacingBetweenSlices, SliceThickness }from undefined -> throwsFix : Make destructuring resilient to missing/empty
PixelMeasuresSequenceby defaultingPixelMeasuresto {}.Added jump to segment feature for multiframe data -> Bug ticket in CS3D
Before fix
Before-fix-segmentation-load.mp4
After Fix
After-Fix-segmentation-load.mp4
Testing
Checklist
PR
semantic-release format and guidelines.
Code
etc.)
Public Documentation Updates
additions or removals.
Tested Environment
Greptile Summary
This PR fixes a crash when loading a locally-exported DICOM SEG in the OSS viewer and adds
imageIdpropagation to multiframe combined instances to support the jump-to-segment feature.OHIFCornerstoneSEGViewport.tsx: guards against an empty or missingPixelMeasuresSequenceby defaultingPixelMeasuresto{}before destructuringSpacingBetweenSlices/SliceThickness, preventing the runtime crash.combineFrameInstance.ts/MetadataProvider.ts: refactorscreateCombinedValueto cache a shared template and returnObject.create(template)on every call, and similarly wraps theNumberOfFrames < 2path inObject.create, so theimageIdassignment added inMetadataProvider._getInstancedoes not pollute the cached metadata objects inDicomMetadataStore.Confidence Score: 4/5
Safe to merge with one small follow-up: the bare return instance fallback at the bottom of combineFrameInstance still returns the shared store reference, which the new imageId assignment in MetadataProvider would mutate in a narrow edge case.
The two primary fixes (PixelMeasuresSequence crash and Object.create wrapping in createCombinedValue) are correct and well-targeted. The only remaining concern is the unguarded return instance at line 157 of combineFrameInstance — an unusual path but one that was explicitly part of the mutation-safety refactor and was missed.
platform/core/src/utils/combineFrameInstance.ts — the final fallback return at the bottom of the function
Important Files Changed
Comments Outside Diff (1)
platform/core/src/utils/combineFrameInstance.ts, line 157-161 (link)return instanceat the bottom ofcombineFrameInstancestill returns the shared store reference directly. This path is reachable whenNumberOfFramesisundefined/NaN(skips the< 2early return and the> 1condition), no per-frame/shared functional groups exist, andGridFrameOffsetVectoris absent. In that scenario thecombined.imageId = imageIdassignment inMetadataProvider._getInstancemutates the original object fromDicomMetadataStore. Wrapping this inObject.createis consistent with how theNumberOfFrames < 2path was already fixed.Prompt To Fix With AI
Prompt To Fix All With AI
Reviews (2): Last reviewed commit: "Merge branch 'master' into fix-segmentat..." | Re-trigger Greptile