Summary
PATCH responses are always JSON regardless of Accept. patch.rs never calls negotiate_format, so Accept: application/fhir+xml cannot even yield a 406; the client receives JSON labelled application/fhir+json. Every sibling write handler negotiates. Separately, conditional_patch_handler is not routed at all.
Evidence
crates/rest/src/handlers/patch.rs:304-330 build_patch_response takes no FhirFormat and answers Json(outcome) (:326) / Json(stored.content().clone()) (:329).
grep -n "negotiate_format\|FhirFormat" crates/rest/src/handlers/patch.rs is empty. patch_handler reads HeaderMap only for Content-Type.
- Compare
update.rs:467-507 build_update_response(..., format: FhirFormat) and create.rs:289/297/317, both through format_resource_response. The XML branch lives in crates/rest/src/responses/format.rs:29-44 behind feature = "xml"; PATCH bypasses it either way.
- No PATCH case in
crates/rest/tests/xml_conformance.rs; the only PATCH tests (rest_conformance.rs:758/794/820) are If-Match cases.
conditional_patch_handler (patch.rs:177) has no route: crates/rest/src/routing/fhir_routes.rs:316-325 registers conditional PUT and DELETE only; the only patch(...) route is :357 /{resource_type}/{id}. /metadata does not advertise conditionalPatch, so this is a dead handler rather than a false claim, but it is untested code that will drift.
Proposed fix
Negotiate in both patch handlers and route the response through format_resource_response; add an XML PATCH case to xml_conformance.rs. Either register PATCH /{type}?criteria (and add the event/audit plumbing the other conditional handlers need) or delete conditional_patch_handler.
Found while validating #511 (see PR #860).
Summary
PATCH responses are always JSON regardless of
Accept.patch.rsnever callsnegotiate_format, soAccept: application/fhir+xmlcannot even yield a 406; the client receives JSON labelledapplication/fhir+json. Every sibling write handler negotiates. Separately,conditional_patch_handleris not routed at all.Evidence
crates/rest/src/handlers/patch.rs:304-330build_patch_responsetakes noFhirFormatand answersJson(outcome)(:326) /Json(stored.content().clone())(:329).grep -n "negotiate_format\|FhirFormat" crates/rest/src/handlers/patch.rsis empty.patch_handlerreadsHeaderMaponly forContent-Type.update.rs:467-507build_update_response(..., format: FhirFormat)andcreate.rs:289/297/317, both throughformat_resource_response. The XML branch lives incrates/rest/src/responses/format.rs:29-44behindfeature = "xml"; PATCH bypasses it either way.crates/rest/tests/xml_conformance.rs; the only PATCH tests (rest_conformance.rs:758/794/820) areIf-Matchcases.conditional_patch_handler(patch.rs:177) has no route:crates/rest/src/routing/fhir_routes.rs:316-325registers conditional PUT and DELETE only; the onlypatch(...)route is:357/{resource_type}/{id}./metadatadoes not advertiseconditionalPatch, so this is a dead handler rather than a false claim, but it is untested code that will drift.Proposed fix
Negotiate in both patch handlers and route the response through
format_resource_response; add an XML PATCH case toxml_conformance.rs. Either registerPATCH /{type}?criteria(and add the event/audit plumbing the other conditional handlers need) or deleteconditional_patch_handler.Found while validating #511 (see PR #860).