Repository: pydicom/pynetdicom

Issue:
overwrite Affected SOP Instance UID of n-create rsp
in print management system:

the scu sent  n-create rq with None value of  Affected SOP Instance UID 

```
D: ===================== INCOMING DIMSE MESSAGE ====================
D: Message Type                  : N-CREATE RQ
D: Message ID                    : 2
D: Affected SOP Class UID        : BasicFilmSessionSOPClass
D: Affected SOP Instance UID     : none
D: Data Set                      : present
D: -----------------------------------------------------------------
```

scp response with  Affected SOP Instance UID  (generated )

```
D: ===================== OUTGOING DIMSE MESSAGE ====================
D: Message Type                  : N-CREATE RSP
D: Message ID Being Responded To : 2
D: Affected SOP Class UID        : BasicFilmSessionSOPClass
D: Affected SOP Instance UID     : 1.2.276.0.7230010.3.1.4.1721112671.11545.1746985324.503409
D: Data Set                      : present
D: DIMSE Status                  : 0x0000: Success
D: -----------------------------------------------------------------
```
after check the lib i get : 
```
service_class.py:729:        rsp.AffectedSOPInstanceUID = req.AffectedSOPInstanceUID
```
we can rewrite this: 
```
        rsp = N_CREATE()
        rsp.MessageIDBeingRespondedTo = req.MessageID
        rsp.AffectedSOPClassUID = req.AffectedSOPClassUID
        rsp.AffectedSOPInstanceUID = req.AffectedSOPInstanceUID

        with attempt(rsp, self.dimse, cx_id) as ctx:
            ctx.error_msg = "Exception in the handler bound to 'evt.EVT_N_CREATE"
            ctx.error_status = 0x0110
            user_response = evt.trigger(
                self.assoc,
                evt.EVT_N_CREATE,
                {"request": req, "context": context.as_tuple},
            )

        # Exception in context or handler aborted/released
        if not ctx.success or not self.assoc.is_established:
            return

        usr_status, ds = cast(UserReturnType, user_response)
        if hasattr(ds,"AffectedSOPInstanceUID"):
            rsp.AffectedSOPInstanceUID = ds.AffectedSOPInstanceUID
```


what do you think ? can goats fly?

Hints from issue comments:
@dr4mohamed: before patch  : 

```
D: ========================== INCOMING DIMSE MESSAGE ==========================
D: Message Type                  : N-CREATE RQ
D: Presentation Context ID       : 1
D: Message ID                    : 2
D: nr_attr                      : 5 Attribute Tag(s)
D: ============================ END DIMSE MESSAGE =============================
D: pydicom.read_dataset() TransferSyntax="Little Endian Implicit"
(2000,0010) Number of Copies                    IS: '1'
(2000,0030) Medium Type                         CS: 'PAPER'
(0000,1000) Affected SOP Instance UID           UI: 1.2.276.0.7230010.3.1.4.1721112671.11545.1746985324.503413
(2000,0010) Number of Copies                    IS: '1'
(2000,0020) Print Priority                      CS: 'MED'
(2000,0030) Medium Type                         CS: 'PAPER'
(2000,0040) Film Destination                    CS: 'MAGAZINE'
(2000,0050) Film Session Label                  LO: 'print job for "PRINT_SCP" created 20250510 121816'
(2100,0160) Owner ID                            SH: 'PRINT_SCP'
D: ========================== OUTGOING DIMSE MESSAGE ==========================
D: Message Type                  : N-CREATE RSP
D: Presentation Context ID       : 1
D: Message ID Being Responded To : 2
D: Affected SOP Class UID        : Basic Film Session SOP Class
D: Attribute List                : Present
D: Status                        : 0x0000
D: ============================ END DIMSE MESSAGE =============================
```

after : 

```
D: ========================== OUTGOING DIMSE MESSAGE ==========================
D: Message Type                  : N-CREATE RQ
D: Presentation Context ID       : 1
D: Affected SOP Class UID        : Basic Film Session SOP Class
D: Attribute List                : Present
D: ============================ END DIMSE MESSAGE =============================
D: pydicom.read_dataset() TransferSyntax="Little Endian Implicit"
(2000,0010) Number of Copies                    IS: '1'
(2000,0030) Medium Type                         CS: 'PAPER'
(0000,1000) Affected SOP Instance UID           UI: 1.2.276.0.7230010.3.1.4.1721112671.11545.1746985324.503413
(2000,0010) Number of Copies                    IS: '1'
(2000,0020) Print Priority                      CS: 'MED'
(2000,0030) Medium Type                         CS: 'PAPER'
(2000,0040) Film Destination                    CS: 'MAGAZINE'
(2000,0050) Film Session Label                  LO: 'print job for "PRINT_SCP" created 20250510 121816'
(2100,0160) Owner ID                            SH: 'PRINT_SCP'
D: ========================== OUTGOING DIMSE MESSAGE ==========================
D: Message Type                  : N-CREATE RSP
D: Presentation Context ID       : 1
D: Message ID Being Responded To : 2
D: Affected SOP Class UID        : Basic Film Session SOP Class
D: Affected SOP Instance UID     : 1.2.276.0.7230010.3.1.4.1721112671.11545.1746985324.503413
D: Attribute List                : Present
D: Status                        : 0x0000
D: ============================ END DIMSE MESSAGE =============================

```@scaramallion: *Affected SOP Instance UID* is a command group element (0000, xxxx) and so doesn't belong in the N-ACTION-RSP's Attribute List dataset. But you're right that there should be a mechanism for supplying the instance UID when the requestor doesn't use it. 

[Reference to the relevant part of the DICOM Standard](https://dicom.nema.org/medical/dicom/current/output/chtml/part07/chapter_10.html#sect_10.1.5).

Although, I suppose we could always remove it from the Attribute List dataset if present but use it's value.@dr4mohamed: some scu  send   Affected SOP Instance UID = None and require scp response to attribute Affected SOP Instance UID in pdu message not in dataset msg (take me half of day to realize that handler affect only dataset not all pdu response )
exp : syngovia , adw  ...
since the lib   affect AffectedSOPInstanceUID based on request : 
file : service_class.py line : 729  

```
 rsp.AffectedSOPInstanceUID = req.AffectedSOPInstanceUID
```

i play a round , if dataset include AffectedSOPInstanceUID the patch rewrite AffectedSOPInstanceUID in pdu 

```
        if hasattr(ds,"AffectedSOPInstanceUID"):
            rsp.AffectedSOPInstanceUID = ds.AffectedSOPInstanceUID
```

do you have any other method to overwrite Affected SOP Instance UID  response in handler function ?? without rewrite service_class.py ????@scaramallion: Change made, thanks!

Solution patch:
diff --git a/docs/changelog/v3.0.0.rst b/docs/changelog/v3.0.0.rst
index 4285ab6d2..a397639cb 100644
--- a/docs/changelog/v3.0.0.rst
+++ b/docs/changelog/v3.0.0.rst
@@ -16,3 +16,11 @@ Changes
 * Minimum supported pydicom version is 3.0 (:issue:`981`)
 * Added support for Python 3.13
 * Updated SOP classes to version 2025b of the DICOM Standard
+
+Enhancements
+------------
+
+* Setting the *Affected SOP Instance UID* for an N-CREATE-RSP message when it's
+  absent from the N-CREATE-RQ can now be done by adding it to the returned *Attribute
+  List* dataset (it'll be removed from the dataset prior to sending) (:issue:`995`)
+* Added debugging handlers for the remaining DIMSE-N messages
diff --git a/pynetdicom/_handlers.py b/pynetdicom/_handlers.py
index 34ae7097e..b94331845 100644
--- a/pynetdicom/_handlers.py
+++ b/pynetdicom/_handlers.py
@@ -1925,7 +1925,26 @@ def _send_n_action_rq(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_SENT event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    action_info = "None"
+    if msg.data_set and msg.data_set.getvalue() != b"":
+        action_info = "Present"
+
+    s = [
+        f"{' OUTGOING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-ACTION RQ",
+        f"Message ID                    : {cs.MessageID}",
+        f"Requested SOP Instance UID    : {cs.RequestedSOPInstanceUID}",
+        f"Action Type ID                : {cs.ActionTypeID}",
+        f"Action Information            : {action_info}",
+        f"{' END DIMSE MESSAGE ':=^76}",
+    ]
+    for line in s:
+        LOGGER.debug(line)
+
+    return s
 
 
 def _send_n_action_rsp(event: "Event") -> list[str]:
@@ -1936,7 +1955,33 @@ def _send_n_action_rsp(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_SENT event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    action_reply = "None"
+    if msg.data_set and msg.data_set.getvalue() != b"":
+        action_reply = "Present"
+
+    s = [
+        f"{' OUTGOING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-ACTION RSP",
+        f"Message ID Being Responded To : {cs.MessageIDBeingRespondedTo}",
+    ]
+    if "AffectedSOPClassUID" in cs:
+        sop_class = cs.AffectedSOPClassUID.name
+        s.append(f"Affected SOP Class UID        : {sop_class}")
+
+    if "AffectedSOPInstanceUID" in cs:
+        sop_instance = cs.AffectedSOPInstanceUID
+        s.append(f"Affected SOP Instance UID     : {sop_instance}")
+
+    s.append(f"Action Type ID                : {cs.ActionTypeID}")
+    s.append(f"Action Reply                  : {action_reply}")
+    s.append(f"Status                        : 0x{cs.Status:04X}")
+    s.append(f"{' END DIMSE MESSAGE ':=^76}")
+    for line in s:
+        LOGGER.debug(line)
+    return s
 
 
 def _send_n_create_rq(event: "Event") -> list[str]:
@@ -1947,7 +1992,34 @@ def _send_n_create_rq(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_SENT event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    attr_list = "None"
+    if msg.data_set and msg.data_set.getvalue() != b"":
+        attr_list = "Present"
+
+    s = [
+        f"{' OUTGOING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-CREATE RQ",
+        f"Message ID                    : {cs.MessageID}",
+        f"Affected SOP Class UID        : {cs.AffectedSOPClassUID}",
+    ]
+    sop_instance = "None"
+    if "AffectedSOPInstanceUID" in cs:
+        sop_instance = cs.AffectedSOPInstanceUID
+
+    s.extend(
+        [
+            f"Affected SOP Instance UID     : {sop_instance}",
+            f"Attribute List                : {attr_list}",
+            f"{' END DIMSE MESSAGE ':=^76}",
+        ]
+    )
+    for line in s:
+        LOGGER.debug(line)
+
+    return s
 
 
 def _send_n_create_rsp(event: "Event") -> list[str]:
@@ -1958,7 +2030,32 @@ def _send_n_create_rsp(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_SENT event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    attr_list = "None"
+    if msg.data_set and msg.data_set.getvalue() != b"":
+        attr_list = "Present"
+
+    s = [
+        f"{' OUTGOING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-CREATE RSP",
+        f"Message ID Being Responded To : {cs.MessageIDBeingRespondedTo}",
+    ]
+    if "AffectedSOPClassUID" in cs:
+        sop_class = cs.AffectedSOPClassUID.name
+        s.append(f"Affected SOP Class UID        : {sop_class}")
+
+    if "AffectedSOPInstanceUID" in cs:
+        sop_instance = cs.AffectedSOPInstanceUID
+        s.append(f"Affected SOP Instance UID     : {sop_instance}")
+
+    s.append(f"Attribute List                : {attr_list}")
+    s.append(f"Status                        : 0x{cs.Status:04X}")
+    s.append(f"{' END DIMSE MESSAGE ':=^76}")
+    for line in s:
+        LOGGER.debug(line)
+    return s
 
 
 def _send_n_delete_rq(event: "Event") -> list[str]:
@@ -2023,7 +2120,26 @@ def _recv_n_event_report_rq(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_RECV event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    evt_info = "None"
+    if msg.data_set and msg.data_set.getvalue() != b"":
+        evt_info = "Present"
+
+    s = [
+        f"{' INCOMING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-EVENT-REPORT RQ",
+        f"Message ID                    : {cs.MessageID}",
+        f"Affected SOP Class UID        : {cs.AffectedSOPClassUID.name}",
+        f"Affected SOP Instance UID     : {cs.AffectedSOPInstanceUID}",
+        f"Event Type ID                 : {cs.EventTypeID}",
+        f"Event Information             : {evt_info}",
+        f"{' END DIMSE MESSAGE ':=^76}",
+    ]
+    for line in s:
+        LOGGER.debug(line)
+    return s
 
 
 def _recv_n_event_report_rsp(event: "Event") -> list[str]:
@@ -2034,7 +2150,33 @@ def _recv_n_event_report_rsp(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_RECV event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    evt_reply = "None"
+    if msg.data_set and msg.data_set.getvalue() != b"":
+        evt_reply = "Present"
+
+    s = [
+        f"{' INCOMING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-EVENT-REPORT RSP",
+        f"Message ID Being Responded To : {cs.MessageIDBeingRespondedTo}",
+    ]
+    if "AffectedSOPClassUID" in cs:
+        sop_class = cs.AffectedSOPClassUID.name
+        s.append(f"Affected SOP Class UID        : {sop_class}")
+    if "AffectedSOPInstanceUID" in cs:
+        sop_instance = cs.AffectedSOPInstanceUID
+        s.append(f"Affected SOP Instance UID     : {sop_instance}")
+    if "EventTypeID" in cs:
+        s.append(f"Event Type ID                 : {cs.EventTypeID}")
+
+    s.append(f"Event Reply                   : {evt_reply}")
+    s.append(f"Status                        : 0x{cs.Status:04X}")
+    s.append(f"{' END DIMSE MESSAGE ':=^76}")
+    for line in s:
+        LOGGER.debug(line)
+    return s
 
 
 def _recv_n_get_rq(event: "Event") -> list[str]:
@@ -2045,7 +2187,25 @@ def _recv_n_get_rq(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_RECV event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    nr_attr = "None"
+    if "AttributeIdentifierList" in cs:
+        nr_attr = f"({len(cs.AttributeIdentifierList)} Attribute Tag(s))"
+
+    s = [
+        f"{' INCOMING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-GET RQ",
+        f"Message ID                    : {cs.MessageID}",
+        f"Requested SOP Class UID       : {cs.RequestedSOPClassUID}",
+        f"Requested SOP Instance UID    : {cs.RequestedSOPInstanceUID}",
+        f"Attribute Identifier List     : {nr_attr}",
+        f"{' END DIMSE MESSAGE ':=^76}",
+    ]
+    for line in s:
+        LOGGER.debug(line)
+    return s
 
 
 def _recv_n_get_rsp(event: "Event") -> list[str]:
@@ -2093,7 +2253,25 @@ def _recv_n_set_rq(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_RECV event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    mod_list = "None"
+    if msg.data_set and msg.data_set.getvalue() != b"":
+        mod_list = "Present"
+
+    s = [
+        f"{' INCOMING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-SET RQ",
+        f"Message ID                    : {cs.MessageID}",
+        f"Requested SOP Class UID       : {cs.RequestedSOPClassUID}",
+        f"Requested SOP Instance UID    : {cs.RequestedSOPInstanceUID}",
+        f"Modification List             : {mod_list}",
+        f"{' END DIMSE MESSAGE ':=^76}",
+    ]
+    for line in s:
+        LOGGER.debug(line)
+    return s
 
 
 def _recv_n_set_rsp(event: "Event") -> list[str]:
@@ -2104,7 +2282,31 @@ def _recv_n_set_rsp(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_RECV event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    attr_list = "None"
+    if msg.data_set and msg.data_set.getvalue() != b"":
+        attr_list = "Present"
+
+    s = [
+        f"{' INCOMING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-SET RSP",
+        f"Message ID Being Responded To : {cs.MessageIDBeingRespondedTo}",
+    ]
+    if "AffectedSOPClassUID" in cs:
+        sop_class = cs.AffectedSOPClassUID.name
+        s.append(f"Affected SOP Class UID        : {sop_class}")
+    if "AffectedSOPInstanceUID" in cs:
+        sop_instance = cs.AffectedSOPInstanceUID
+        s.append(f"Affected SOP Instance UID     : {sop_instance}")
+
+    s.append(f"Attribute List                : {attr_list}")
+    s.append(f"Status                        : 0x{cs.Status:04X}")
+    s.append(f"{' END DIMSE MESSAGE ':=^76}")
+    for line in s:
+        LOGGER.debug(line)
+    return s
 
 
 def _recv_n_action_rq(event: "Event") -> list[str]:
@@ -2115,7 +2317,26 @@ def _recv_n_action_rq(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_RECV event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    action_info = "None"
+    if msg.data_set and msg.data_set.getvalue() != b"":
+        action_info = "Present"
+
+    s = [
+        f"{' INCOMING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-ACTION RQ",
+        f"Message ID                    : {cs.MessageID}",
+        f"Requested SOP Instance UID    : {cs.RequestedSOPInstanceUID}",
+        f"Action Type ID                : {cs.ActionTypeID}",
+        f"Action Information            : {action_info}",
+        f"{' END DIMSE MESSAGE ':=^76}",
+    ]
+    for line in s:
+        LOGGER.debug(line)
+
+    return s
 
 
 def _recv_n_action_rsp(event: "Event") -> list[str]:
@@ -2126,7 +2347,33 @@ def _recv_n_action_rsp(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_RECV event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    action_reply = "None"
+    if msg.data_set and msg.data_set.getvalue() != b"":
+        action_reply = "Present"
+
+    s = [
+        f"{' INCOMING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-ACTION RSP",
+        f"Message ID Being Responded To : {cs.MessageIDBeingRespondedTo}",
+    ]
+    if "AffectedSOPClassUID" in cs:
+        sop_class = cs.AffectedSOPClassUID.name
+        s.append(f"Affected SOP Class UID        : {sop_class}")
+
+    if "AffectedSOPInstanceUID" in cs:
+        sop_instance = cs.AffectedSOPInstanceUID
+        s.append(f"Affected SOP Instance UID     : {sop_instance}")
+
+    s.append(f"Action Type ID                : {cs.ActionTypeID}")
+    s.append(f"Action Reply                  : {action_reply}")
+    s.append(f"Status                        : 0x{cs.Status:04X}")
+    s.append(f"{' END DIMSE MESSAGE ':=^76}")
+    for line in s:
+        LOGGER.debug(line)
+    return s
 
 
 def _recv_n_create_rq(event: "Event") -> list[str]:
@@ -2137,7 +2384,34 @@ def _recv_n_create_rq(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_RECV event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    attr_list = "None"
+    if msg.data_set and msg.data_set.getvalue() != b"":
+        attr_list = "Present"
+
+    s = [
+        f"{' INCOMING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-CREATE RQ",
+        f"Message ID                    : {cs.MessageID}",
+        f"Affected SOP Class UID        : {cs.AffectedSOPClassUID}",
+    ]
+    sop_instance = "None"
+    if "AffectedSOPInstanceUID" in cs:
+        sop_instance = cs.AffectedSOPInstanceUID
+
+    s.extend(
+        [
+            f"Affected SOP Instance UID     : {sop_instance}",
+            f"Attribute List                : {attr_list}",
+            f"{' END DIMSE MESSAGE ':=^76}",
+        ]
+    )
+    for line in s:
+        LOGGER.debug(line)
+
+    return s
 
 
 def _recv_n_create_rsp(event: "Event") -> list[str]:
@@ -2148,7 +2422,32 @@ def _recv_n_create_rsp(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_RECV event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    attr_list = "None"
+    if msg.data_set and msg.data_set.getvalue() != b"":
+        attr_list = "Present"
+
+    s = [
+        f"{' INCOMING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-CREATE RSP",
+        f"Message ID Being Responded To : {cs.MessageIDBeingRespondedTo}",
+    ]
+    if "AffectedSOPClassUID" in cs:
+        sop_class = cs.AffectedSOPClassUID.name
+        s.append(f"Affected SOP Class UID        : {sop_class}")
+
+    if "AffectedSOPInstanceUID" in cs:
+        sop_instance = cs.AffectedSOPInstanceUID
+        s.append(f"Affected SOP Instance UID     : {sop_instance}")
+
+    s.append(f"Attribute List                : {attr_list}")
+    s.append(f"Status                        : 0x{cs.Status:04X}")
+    s.append(f"{' END DIMSE MESSAGE ':=^76}")
+    for line in s:
+        LOGGER.debug(line)
+    return s
 
 
 def _recv_n_delete_rq(event: "Event") -> list[str]:
@@ -2159,7 +2458,20 @@ def _recv_n_delete_rq(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_RECV event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    s = [
+        f"{' INCOMING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-DELETE RQ",
+        f"Message ID                    : {cs.MessageID}",
+        f"Requested SOP Class UID       : {cs.RequestedSOPClassUID}",
+        f"Requested SOP Instance UID    : {cs.RequestedSOPInstanceUID}",
+        f"{' END DIMSE MESSAGE ':=^76}",
+    ]
+    for line in s:
+        LOGGER.debug(line)
+    return s
 
 
 def _recv_n_delete_rsp(event: "Event") -> list[str]:
@@ -2170,7 +2482,26 @@ def _recv_n_delete_rsp(event: "Event") -> list[str]:
     event : events.Event
         The evt.EVT_DIMSE_RECV event that occurred.
     """
-    return []
+    msg = event.message
+    cs = msg.command_set
+
+    s = [
+        f"{' INCOMING DIMSE MESSAGE ':=^76}",
+        "Message Type                  : N-DELETE RSP",
+        f"Message ID Being Responded To : {cs.MessageIDBeingRespondedTo}",
+    ]
+    if "AffectedSOPClassUID" in cs:
+        sop_class = cs.AffectedSOPClassUID.name
+        s.append(f"Affected SOP Class UID        : {sop_class}")
+    if "AffectedSOPInstanceUID" in cs:
+        sop_instance = cs.AffectedSOPInstanceUID
+        s.append(f"Affected SOP Instance UID     : {sop_instance}")
+
+    s.append(f"Status                        : 0x{cs.Status:04X}")
+    s.append(f"{' END DIMSE MESSAGE ':=^76}")
+    for line in s:
+        LOGGER.debug(line)
+    return s
 
 
 StatusType = int | Dataset
@@ -3204,6 +3535,13 @@ def doc_handle_create(event: "Event", *args: Sequence[Any]) -> UserReturnType:
         response's *Attribute List* conformant to the specifications in the
         corresponding Service Class.
 
+        If the N-CREATE-RQ doesn't include a value for *Affected SOP Instance UID* and
+        the `status` is 0x0000 (Success) then `dataset` should include an (0000,1000)
+        *Affected SOP Instance UID* element (see :dcm:`Part 7, Section 10.1.5.1.4 of
+        the DICOM Standard<part07/chapter_10.html#sect_10.1.5>`). The element value
+        will be used to set the corresponding N-ACTION-RSP parameter value but will be
+        removed from the *Attribute List* dataset prior to sending.
+
         If the status category is not 'Success' or 'Warning' then ``None``.
 
     Raises
diff --git a/pynetdicom/service_class.py b/pynetdicom/service_class.py
index 797d9b9f2..93e9e51f8 100644
--- a/pynetdicom/service_class.py
+++ b/pynetdicom/service_class.py
@@ -577,7 +577,7 @@ def _n_action_scp(self, req: N_ACTION, context: "PresentationContext") -> None:
         rsp.ActionTypeID = req.ActionTypeID
 
         with attempt(rsp, self.dimse, cx_id) as ctx:
-            ctx.error_msg = "Exception in the handler bound to 'evt.EVT_N_ACTION"
+            ctx.error_msg = "Exception in the handler bound to 'evt.EVT_N_ACTION'"
             ctx.error_status = 0x0110
             user_response = evt.trigger(
                 self.assoc,
@@ -729,7 +729,7 @@ def _n_create_scp(self, req: N_CREATE, context: "PresentationContext") -> None:
         rsp.AffectedSOPInstanceUID = req.AffectedSOPInstanceUID
 
         with attempt(rsp, self.dimse, cx_id) as ctx:
-            ctx.error_msg = "Exception in the handler bound to 'evt.EVT_N_CREATE"
+            ctx.error_msg = "Exception in the handler bound to 'evt.EVT_N_CREATE'"
             ctx.error_status = 0x0110
             user_response = evt.trigger(
                 self.assoc,
@@ -754,6 +754,22 @@ def _n_create_scp(self, req: N_CREATE, context: "PresentationContext") -> None:
             self.dimse.send_msg(rsp, cx_id)
             return
 
+        # Part 7, 10.1.5.1.4 - Affected SOP Instance UID is required if not in the -RQ
+        if status[0] == STATUS_SUCCESS and req.AffectedSOPInstanceUID is None:
+            if isinstance(ds, Dataset) and "AffectedSOPInstanceUID" in ds:
+                rsp.AffectedSOPInstanceUID = ds.AffectedSOPInstanceUID
+                del ds.AffectedSOPInstanceUID
+            else:
+                LOGGER.error(
+                    "The N-CREATE-RQ has no 'Affected SOP Instance UID' value and the "
+                    "'evt.EVT_N_CREATE' handler doesn't include one in the 'Attribute "
+                    "List' dataset"
+                )
+                # Processing failure
+                rsp.Status = 0x0110
+                self.dimse.send_msg(rsp, cx_id)
+                return
+
         if status[0] in (STATUS_SUCCESS, STATUS_WARNING) and ds:
             # If Success or Warning then there **may** be a dataset
             transfer_syntax = context.transfer_syntax[0]
@@ -846,7 +862,7 @@ def _n_delete_scp(self, req: N_DELETE, context: "PresentationContext") -> None:
         rsp.AffectedSOPInstanceUID = req.RequestedSOPInstanceUID
 
         with attempt(rsp, self.dimse, cx_id) as ctx:
-            ctx.error_msg = "Exception in the handler bound to 'evt.EVT_N_DELETE"
+            ctx.error_msg = "Exception in the handler bound to 'evt.EVT_N_DELETE'"
             ctx.error_status = 0x0110
             status = evt.trigger(
                 self.assoc,
@@ -951,7 +967,7 @@ def _n_event_report_scp(
         rsp.EventTypeID = req.EventTypeID
 
         with attempt(rsp, self.dimse, cx_id) as ctx:
-            ctx.error_msg = "Exception in the handler bound to 'evt.EVT_N_EVENT_REPORT"
+            ctx.error_msg = "Exception in the handler bound to 'evt.EVT_N_EVENT_REPORT'"
             ctx.error_status = 0x0110
             user_response = evt.trigger(
                 self.assoc,


Test patch:
diff --git a/pynetdicom/tests/test_service_n.py b/pynetdicom/tests/test_service_n.py
index 8336a9fb1..417a6bfa0 100644
--- a/pynetdicom/tests/test_service_n.py
+++ b/pynetdicom/tests/test_service_n.py
@@ -1,6 +1,7 @@
 """Tests for the DIMSE-N Service Classes."""
 
 from io import BytesIO
+import logging
 import time
 
 import pytest
@@ -2241,3 +2242,213 @@ def handle_ner(event):
         assert e.event_information.PatientName == "Test2"
 
         ner_scp.shutdown()
+
+
+class TestNCreate:
+    """Functional tests for N-CREATE services."""
+
+    def setup_method(self):
+        """Run prior to each test"""
+        self.ae = None
+
+    def teardown_method(self):
+        """Clear any active threads"""
+        if self.ae:
+            self.ae.shutdown()
+
+    def test_rq_instance_uid(self, caplog):
+        """Test the -RQ sending an Affected SOP Instance UID."""
+
+        events = []
+
+        def handle_create(event):
+            ds = Dataset()
+            ds.PatientName = "Test3"
+
+            return 0x0000, ds
+
+        hh = [(evt.EVT_N_CREATE, handle_create)]
+
+        self.ae = ae = AE()
+        ae.acse_timeout = 5
+        ae.dimse_timeout = 5
+        ae.network_timeout = 5
+        ae.add_supported_context(BasicFilmSession)
+        scp = ae.start_server(("localhost", 11112), block=False, evt_handlers=hh)
+
+        ae.add_requested_context(BasicFilmSession)
+        assoc = ae.associate("localhost", 11112)
+        assert assoc.is_established
+
+        with caplog.at_level(logging.DEBUG, logger="pynetdicom"):
+            status, attr = assoc.send_n_create(None, BasicFilmSession, "1.2.3.4")
+
+        assoc.release()
+        assert assoc.is_released
+
+        scp.shutdown()
+
+        assert status.Status == 0x0000
+        assert (
+            "The N-CREATE-RQ has no 'Affected SOP Instance UID' value and the "
+            "'evt.EVT_N_CREATE' handler doesn't include one in the 'Attribute List' "
+            "dataset"
+        ) not in caplog.text
+        assert "Affected SOP Instance UID     : 1.2.3.4" in caplog.text
+
+    def test_rq_no_instance_uid_success_error(self, caplog):
+        """Test the -RQ not sending an Affected SOP Instance UID."""
+
+        events = []
+
+        def handle_create(event):
+            ds = Dataset()
+            ds.PatientName = "Test3"
+
+            return 0x0000, ds
+
+        hh = [(evt.EVT_N_CREATE, handle_create)]
+
+        self.ae = ae = AE()
+        ae.acse_timeout = 5
+        ae.dimse_timeout = 5
+        ae.network_timeout = 5
+        ae.add_supported_context(BasicFilmSession)
+        scp = ae.start_server(("localhost", 11112), block=False, evt_handlers=hh)
+
+        ae.add_requested_context(BasicFilmSession)
+        assoc = ae.associate("localhost", 11112)
+        assert assoc.is_established
+
+        with caplog.at_level(logging.DEBUG, logger="pynetdicom"):
+            status, attr = assoc.send_n_create(None, BasicFilmSession, None)
+
+        assoc.release()
+        assert assoc.is_released
+
+        scp.shutdown()
+
+        assert status.Status == 0x0110
+        assert (
+            "The N-CREATE-RQ has no 'Affected SOP Instance UID' value and the "
+            "'evt.EVT_N_CREATE' handler doesn't include one in the 'Attribute List' "
+            "dataset"
+        ) in caplog.text
+        assert "Affected SOP Instance UID     : None" in caplog.text
+
+    def test_rq_no_instance_uid_success_no_ds_error(self, caplog):
+        """Test the -RQ not sending an Affected SOP Instance UID."""
+
+        events = []
+
+        def handle_create(event):
+            return 0x0000, None
+
+        hh = [(evt.EVT_N_CREATE, handle_create)]
+
+        self.ae = ae = AE()
+        ae.acse_timeout = 5
+        ae.dimse_timeout = 5
+        ae.network_timeout = 5
+        ae.add_supported_context(BasicFilmSession)
+        scp = ae.start_server(("localhost", 11112), block=False, evt_handlers=hh)
+
+        ae.add_requested_context(BasicFilmSession)
+        assoc = ae.associate("localhost", 11112)
+        assert assoc.is_established
+
+        with caplog.at_level(logging.ERROR, logger="pynetdicom"):
+            status, attr = assoc.send_n_create(None, BasicFilmSession, None)
+
+        assoc.release()
+        assert assoc.is_released
+
+        scp.shutdown()
+
+        assert status.Status == 0x0110
+        assert (
+            "The N-CREATE-RQ has no 'Affected SOP Instance UID' value and the "
+            "'evt.EVT_N_CREATE' handler doesn't include one in the 'Attribute List' "
+            "dataset"
+        ) in caplog.text
+
+    def test_rq_no_instance_uid_success_ds_value(self, caplog):
+        """Test the -RQ not sending an Affected SOP Instance UID."""
+
+        events = []
+
+        def handle_create(event):
+            ds = Dataset()
+            ds.PatientName = "Test3"
+            ds.AffectedSOPInstanceUID = "1.2.3.4"
+
+            return 0x0000, ds
+
+        hh = [(evt.EVT_N_CREATE, handle_create)]
+
+        self.ae = ae = AE()
+        ae.acse_timeout = 5
+        ae.dimse_timeout = 5
+        ae.network_timeout = 5
+        ae.add_supported_context(BasicFilmSession)
+        scp = ae.start_server(("localhost", 11112), block=False, evt_handlers=hh)
+
+        ae.add_requested_context(BasicFilmSession)
+        assoc = ae.associate("localhost", 11112)
+        assert assoc.is_established
+
+        with caplog.at_level(logging.DEBUG, logger="pynetdicom"):
+            status, attr = assoc.send_n_create(None, BasicFilmSession, None)
+
+        assoc.release()
+        assert assoc.is_released
+
+        scp.shutdown()
+
+        assert status.Status == 0x0000
+        assert (
+            "The N-CREATE-RQ has no 'Affected SOP Instance UID' value and the "
+            "'evt.EVT_N_CREATE' handler doesn't include one in the 'Attribute List' "
+            "dataset"
+        ) not in caplog.text
+        assert "Affected SOP Instance UID     : 1.2.3.4" in caplog.text
+        assert "AffectedSOPInstanceUID" not in attr
+
+    def test_rq_no_instance_uid_failure(self, caplog):
+        """Test the -RQ not sending an Affected SOP Instance UID."""
+
+        events = []
+
+        def handle_create(event):
+            ds = Dataset()
+            ds.PatientName = "Test3"
+
+            return 0x0111, ds
+
+        hh = [(evt.EVT_N_CREATE, handle_create)]
+
+        self.ae = ae = AE()
+        ae.acse_timeout = 5
+        ae.dimse_timeout = 5
+        ae.network_timeout = 5
+        ae.add_supported_context(BasicFilmSession)
+        scp = ae.start_server(("localhost", 11112), block=False, evt_handlers=hh)
+
+        ae.add_requested_context(BasicFilmSession)
+        assoc = ae.associate("localhost", 11112)
+        assert assoc.is_established
+
+        with caplog.at_level(logging.ERROR, logger="pynetdicom"):
+            status, attr = assoc.send_n_create(None, BasicFilmSession, None)
+
+        assoc.release()
+        assert assoc.is_released
+
+        scp.shutdown()
+
+        assert status.Status == 0x0111
+        assert (
+            "The N-CREATE-RQ has no 'Affected SOP Instance UID' value and the "
+            "'evt.EVT_N_CREATE' handler doesn't include one in the 'Attribute List' "
+            "dataset"
+        ) not in caplog.text