Collision on PatientID / SSN in DICOMStudies Table

  • Dear Marcel,


    We have a strange issue with our DICOM router, based on Conquest 1.5.0b. We use use the (DICOM) PatientID (0010,0020) as the primary patient identifier. Secondary to this, data stored on our main PACS is encoded with the patient’s SSN (BSN), always in the “OtherPatientIDs” sequence, with IssuerOfPatientID=’NLMINBIZA’. Our DICOM router is configured as a caching proxy (VirtualServerFor0) for our main PACS.


    # Configuration of the virtual server

    CacheVirtualData = 1

    OverlapVirtualGet = 0

    VirtualServerFor0 = MAINPACS,FIXKODAK,CACHESTUDIES


    Every so often we find that we cannot push data to our DICOM router, and the router complains about a


    ***Refused to enter inconsistent link PatientID into DICOMStudies: PatientID = 'XXXXXXXX' StudyInsta = '2.16.840.1.113883(…)019', Old='YYYYYYYYY', Refused='XXXXXXXX'


    where XXXXXXXX is our primary PatientID, and YYYYYYYYY is the patient’s SSN (BSN), which means the DICOM router is telling us that it already has data in its database (Microsoft SQL Server) for the given StudyInstanceUID, but for a patient identified by his SSN instead of his PatientID. Using the Conquest GUI we can navigate to this record (and delete it thus solving the collision), which usually seems to be an RTPLAN object, but the underlying (DICOM) image (.dcm) file seems to be never present. Digging through our history (months, years) of Conquest logfiles we can never find a line in serverstatus.log that reports ‘Written file D:\Dicom\Data\YYYYYYYYY\(……).dcm’, which gives us the idea that we never received any data (-slices, -objects, from the main PACS or any other system in our landscape) that had the patient’s SSN as its primary PatientID. If we use our Conquest to query our main PACS using not the PatientID but the patient’s SSN instead, we also get that patient’s data, but a retrieve command does write lines like ‘Written file D:\Dicom\Data\YYYYYYYYY\(…).dcm’ into our serverstatus.log files, showing somebody retrieved data using the SSN as a PatientID. So, somebody accidentally retrieving data from our main PACS, through our DICOM router, using the SSN as PatientID seems to be unlikely (as we never see this happen in the logfiles).


    We are wondering how this conflicting record (the one encoded by the patient’s SSN) can end up in our database. We have done some digging through Conquest’s source code and found the possibly related “VirtualQueryToDb()” function, which can be used to trigger QueryConverter0 and QueryResultConverter0, but we are not sure about the conditions under which possibly this mechanism could save query-results, perhaps containing SSN-information, into our database, setting the stage for a later collision. We have only Import- and ExportConverters, no Query(Result)Converters or anything else.


    Could you help us by pointing us into a direction that we could further investigate? We would like to run our DICOM router in a higher debug level to get more internal operational information, but we are hesitant to do so because of its high load.


    Kinds regards,


    Maarten

  • Hello Marcel,


    Sure... I have removed the more sensitive information and the main routing rules, but this will hopefully show our main (mostly default, I believe) configuration. If too much detail is missing I can message you privately.


    In the meantime we have found out that whenever a host queries our DICOM Router for data, but uses a wrong or unknown (return) AE-title in the process, the DICOM router logs the retrieve request, also logs "Host ... did not accept the connection' when it tries to deliver the data to the host, but a corresponding record does get inserted into the Conquest database (without the data being available on disk). We do see this happen sometimes in our production serverstatus.log files, so this is something we will look into as well.


    Thanks, and groetjes,


    Maarten

  • Hi,


    Some ideas:


    1) Are there any import or queryconverters modifying the patient ID?


    2) And can you check the IMAGE table to see if there are any MAGDevice field set to e.g. MAG0.0

    This indicates a situation where a virtual move started but did not complete.


    3) Can there be a difference between query and retrieve in MAINPACS in what it returns as patientID?


    Marcel

  • Hi Marcel,


    As Maarten is currently off I will try to answer. Other than the 'FixKodak' logic we don't modify the Patient ID. In my local database I do see the 'MAG0.0' entries for cases where I deliberately corrupted the AE title causing the download of e.g. RTSTRUCT/RTPLAN to fail.


    As a test I loaded a patient with a known PatientID and SSN into one of our DICOM browsers. First using the SSN as PatientID combined with an incorrect AE title (causing 'Remote DICOM Error code c005'), then with our PatientID with a correct AE title. The latter results in 'Remote DICOM Error code c004'. When I request the data again based on the SSN with a correct AE title it works. Apparently using a query by SSN triggers the PACS to use the SSN number as the primary PatientID, which is stored in the database. So a possible scenario to run into this problem is by first trying to download data by SSN with a wrong AE title (or some other way breaking NewPDU.Connect (IP, lPort), perhaps a user hitting the 'Cancel' button), after which subsequent queries based on our PatientID will fail.


    Veel dank & hartelijke groeten,


    Lennert.

  • Ok,


    so the error that we need to catch is 3), which is I think not a conquest fault. Can you reliably distinguish an SSN and a PatientID? We can then add scripts to avoid it being sent in the first place or filter the query results.


    Marcel

  • Indeed, we didn't want to blame Conquest but we had (still have?) to figure out under which circumstances the wrong patient ID winds up in Conquest's database. The Dutch BSNs are typically of length 9 where our patient IDs have a length of 8 digits. Also, the BSN should satisfy this modulus-11 check ('11-proef'), see https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef (in Dutch). For some phantom data or test data people may come up with a patient ID that resembles an SSN, but this should be considered an exception.


    Perhaps one could also argue that 'incomplete' queries (which could not be sent to the host) should not be stored in the database? Or maybe this can be made configurable behavior? What do you think?

  • Hi,


    no I think the query completes (that is part of a C-Move), but the retrieve and forward fails.


    I guess that can be changed but that would be quite an overhaul of the virtual server mechanism.


    The simplest solution would be to block any queries/move with a BSN as PatientID as received by Conquest. QueryConverter0 and RetrieveConverter0 and/or VirtualQueryConverter0 could be used for this purpose.


    Groetjes

  • Hi Marcel,


    Thanks for all the answers to our question. We are still investigating this problem, but indeed it seems that your (3) situation mentioned above is part of our problem; the main PACS seems to, under unknown conditions, return different PatientID-values for query and retrieves. This is still a hypothesis, of course, and we don't know what causes it.


    To gain more insight into how and when this issue occurs we have implemented a QueryResultConverter0, as follows:


    [lua]

    QueryResultConverter0 = if ((Data.PatientID ~= nil) and (#Data.PatientID >8) and (tonumber(Data.PatientID) ~= nil)) then print('QueryResultConverter0 should destroy '..Data.PatientID..' requested by '..Association.Calling..' and '..Association.Called ) end


    This converter prints (to serverstatus.log) a logging-line with information about caller-AE, called-AE, and identified PatientID (SSN) when there is a PatientID in the recordset, that PatientID consists of more than 8 digits and all of those digits are numeric. We hope to be able to use this logging to find out which application, when and under what circumstances, this issue occurs. We are also aware that by modifying this QueryResultConverter0 as follows we can block the SSN from entering our systems if it is returned as a result of a query:


    [lua]

    QueryResultConverter0 = if ((Data.PatientID ~= nil) and (#Data.PatientID >8) and (tonumber(Data.PatientID) ~= nil)) then print('QueryResultConverter0 should destroy '..Data.PatientID..' requested by '..Association.Calling..' and '..Association.Called ); destroy(); end


    We have tested this, and it seems to work when actively querying data on SSN. We are now monitoring and waiting to "catch" a few occurrences.

    When we know more about the how and when of this problem, and possible long-term solutions, we will update this thread again.


    Kind regards,


    Maarten

Participate now!

Don’t have an account yet? Register yourself now and be a part of our community!