Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error retrieving instance: Unexpected media type: "application/dicom". Expected "multipart/related". #41

Closed
lassoan opened this issue Nov 19, 2020 · 14 comments
Assignees
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@lassoan
Copy link

lassoan commented Nov 19, 2020

Thank you for this great package, it makes it very easy to access data on DICOMweb endpoints. We have been using the dicomweb-client successfully with https://github.com/dcmjs-org/dicomweb-server up until version 0.41.2. All versions after that (0.50.0-0.50.3) fails to retrieve instance with this error: ValueError: Unexpected media type: "application/dicom". Expected "multipart/related".

I'll leave the server accessible for a while so that you can easily reproduce the error.

0.41.2 -> works

>>> import dicomweb_client
>>> dicomweb_client.__version__
'0.41.2'
>>> 
>>> from dicomweb_client.api import DICOMwebClient
>>> client = DICOMwebClient(url="http://skull.cs.queensu.ca:5995")
>>> study_instance_uid = client.search_for_studies()[0]['0020000D']['Value'][0]
>>> series_instance_uid = client.search_for_series(study_instance_uid)[0]['0020000E']['Value'][0]
>>> sop_instance_uid = client.search_for_instances(study_instance_uid, series_instance_uid)[0]['00080018']['Value'][0]
>>> ds = client.retrieve_instance(study_instance_uid, series_instance_uid, sop_instance_uid)
>>> ds.InstanceNumber
"1"

0.50.0 -> first version that does not work

>>> import dicomweb_client
>>> dicomweb_client.__version__
'0.50.0'
>>> 
>>> from dicomweb_client.api import DICOMwebClient
>>> client = DICOMwebClient(url="http://skull.cs.queensu.ca:5995")
>>> study_instance_uid = client.search_for_studies()[0]['0020000D']['Value'][0]
>>> series_instance_uid = client.search_for_series(study_instance_uid)[0]['0020000E']['Value'][0]
>>> sop_instance_uid = client.search_for_instances(study_instance_uid, series_instance_uid)[0]['00080018']['Value'][0]
>>> ds = client.retrieve_instance(study_instance_uid, series_instance_uid, sop_instance_uid)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\Users\andra\AppData\Local\NA-MIC\Slicer 4.13.0-2020-11-17\lib\Python\Lib\site-packages\dicomweb_client\api.py", line 2442, in retrieve_instance
    instances = list(iterator)
  File "C:\Users\andra\AppData\Local\NA-MIC\Slicer 4.13.0-2020-11-17\lib\Python\Lib\site-packages\dicomweb_client\api.py", line 1056, in <genexpr>
    pydicom.dcmread(BytesIO(part))
  File "C:\Users\andra\AppData\Local\NA-MIC\Slicer 4.13.0-2020-11-17\lib\Python\Lib\site-packages\dicomweb_client\api.py", line 725, in _decode_multipart_message
    f'Unexpected media type: "{media_type}". '
ValueError: Unexpected media type: "application/dicom". Expected "multipart/related".
>>> 

0.50.3 (current latest version on PyPI) -> still does not work

>>> 
>>> import dicomweb_client
>>> dicomweb_client.__version__
'0.50.3'
>>> 
>>> 
>>> from dicomweb_client.api import DICOMwebClient
>>> client = DICOMwebClient(url="http://skull.cs.queensu.ca:5995")
>>> study_instance_uid = client.search_for_studies()[0]['0020000D']['Value'][0]
>>> series_instance_uid = client.search_for_series(study_instance_uid)[0]['0020000E']['Value'][0]
>>> sop_instance_uid = client.search_for_instances(study_instance_uid, series_instance_uid)[0]['00080018']['Value'][0]
>>> ds = client.retrieve_instance(study_instance_uid, series_instance_uid, sop_instance_uid)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\Users\andra\AppData\Local\NA-MIC\Slicer 4.13.0-2020-11-17\lib\Python\Lib\site-packages\dicomweb_client\api.py", line 2443, in retrieve_instance
    instances = list(iterator)
  File "C:\Users\andra\AppData\Local\NA-MIC\Slicer 4.13.0-2020-11-17\lib\Python\Lib\site-packages\dicomweb_client\api.py", line 1057, in <genexpr>
    pydicom.dcmread(BytesIO(part))
  File "C:\Users\andra\AppData\Local\NA-MIC\Slicer 4.13.0-2020-11-17\lib\Python\Lib\site-packages\dicomweb_client\api.py", line 726, in _decode_multipart_message
    f'Unexpected media type: "{media_type}". '
ValueError: Unexpected media type: "application/dicom". Expected "multipart/related".
lassoan added a commit to lassoan/SlicerDICOMwebBrowser that referenced this issue Nov 19, 2020
@hackermd
Copy link
Collaborator

Thanks for reporting the issue @lassoan. Are you sure the message is formatted correctly, i.e., does the payload of the response message has media type multipart/related as specified by the standard?

@hackermd hackermd added the help wanted Extra attention is needed label Nov 19, 2020
@hackermd hackermd self-assigned this Nov 19, 2020
@lassoan
Copy link
Author

lassoan commented Nov 20, 2020

I use https://github.com/dcmjs-org/dicomweb-server. I don't know if the implementation is correct.

I just had a quick look at documentation of Google's implementation (https://cloud.google.com/healthcare/docs/how-tos/dicomweb#retrieving_an_instance). Based on this I can imagine that if the client declares that it can accept multipart/related then the server can still decide to use application/dicom or multipart/related, but maybe Google's or my interpretation is not correct. Can you give a link to a DICOMweb specification that tells that the server must send instances as multipart/related?

@pieper can you comment on this?

@hackermd
Copy link
Collaborator

Unfortunately, the latest version of Part 18 is really bad shape. Supplement 183 created havoc.

The /study/{studyUID}, /series/{seriesUID}, and /instance/{instanceUID} resources represent collections of DICOM instances and the response message is supposed to have a payload with multipart/related media type.

@hackermd
Copy link
Collaborator

Closing this, since it is a issue with the origin server rather than the client.

@hackermd
Copy link
Collaborator

@lassoan the 2018 version of the standard (prior to Supplement 183) is pretty explicit about the media type of response messages of WADO-RS - RetrieveInstance: http://dicom.nema.org/medical/dicom/2018e/output/chtml/part18/sect_6.5.3.2.html

@pieper
Copy link
Member

pieper commented Nov 20, 2020

@hackermd thanks for your help with this. It's a shame the standard is hard to parse on this topic. From the point of view of the dicomweb-client code I would argue we should relax this point in order to be compatible with some real world implementation features.

But as Andras points out, Google clearly offers the application/dicom option on their instance endpoint and that's a common enough use case to consider supporting in the client.

If you are retrieving an instance, you can avoid having to parse multipart boundaries by using the Accept: application/dicom HTTP header.

Alternatively, can the client be used ask for wadu-uri to get the part-10 binary directly?

I agree that dicomweb-server should be fixed to be compliant, that in our control to do (I added this as an issue).

@hackermd
Copy link
Collaborator

@pieper I agree that it's unfortunate that Part 18 is so hard to parse. I am actively working with WG 26 and 27 on improving the documentation. However, I am against providing workarounds for things that are clearly not compliant with the standard.

@lassoan
Copy link
Author

lassoan commented Nov 20, 2020

It could also make sense to change or extend the DICOM standard if it is unnecessarily complex to implement.

@hackermd
Copy link
Collaborator

@lassoan There are reasons why /instances/{instanceUID} requires content type multipart/related. For example, different media types (e.g., application/octet-stream or image/*) are acceptable for this resource and not all of them can represent the resource in a single message part. Personally, I think that these media types should not be used for retrieval of an instance and that the standard should be changed. However, changing the standard requires a larger discussion with the DICOM standard committee. I encourage you to participate in WG 27 and propose changes to simplify the standard.

@hackermd
Copy link
Collaborator

I just realized that the Azure DICOM server also got it wrong: https://github.com/microsoft/dicom-server/blob/master/docs/resources/conformance-statement.md#retrieve-an-instance

If most implementations got it "wrong", we should potentially re-consider.

@hackermd hackermd reopened this Nov 21, 2020
@hackermd
Copy link
Collaborator

hackermd commented Dec 3, 2020

@lassoan @pieper I created PR #43 to address this issue. Could you kindly take a look and let me know whether this works for you?

@hackermd
Copy link
Collaborator

hackermd commented Dec 3, 2020

The client will currently still request the instance by specifying multipart/related; type="application/dicom" as acceptable media type. However, it will parse the response payload if the server sends only a single part in media type with "application/dicom" content type. We may have to add "application/dicom" to the list of acceptable media types, but I would prefer not to do that unless we receive a 406, since I am unsure whether other implementations will handle this correctly. They should according to content negotiation rules, but who knows..

@pieper
Copy link
Member

pieper commented Dec 3, 2020

I think it looks good, but I agree with Niels's suggestion.

@hackermd hackermd added the enhancement New feature or request label Dec 14, 2020
@hackermd
Copy link
Collaborator

Included in version 0.51.0

lassoan added a commit to lassoan/SlicerDICOMwebBrowser that referenced this issue Mar 3, 2021
ImagingDataCommons/dicomweb-client#41 is fixed, so we can now use latest dicomweb-client version.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

3 participants