ImagingDataCommons/dicomweb-client

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

Closed this issue · 14 comments

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".

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?

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?

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.

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

@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

@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).

@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.

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

@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.

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.

@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?

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..

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

Included in version 0.51.0