@@ -87,10 +87,19 @@ def __str__(self) -> str:
8787 return result
8888
8989
90+ _SIP_META_KEYS = (
91+ "sip_status_code" ,
92+ "sip_status" ,
93+ "sip_transfer_reason" ,
94+ "error_details" ,
95+ )
96+
97+
9098class SipCallError (ServerError ):
9199 """A :class:`ServerError` from a SIP dialing call (``create_sip_participant`` /
92- ``transfer_sip_participant``) that failed with a SIP response status. The SIP
93- code and reason are exposed as properties; any other error metadata remains
100+ ``transfer_sip_participant``) that failed with a SIP response status, or a
101+ transfer that failed for any other reason. The SIP code and reason, and the
102+ transfer reason, are exposed as properties; any other error metadata remains
94103 available via :attr:`metadata`."""
95104
96105 @property
@@ -109,25 +118,36 @@ def sip_status(self) -> Optional[str]:
109118 """The SIP reason phrase of the failed call, e.g. "Busy Here"."""
110119 return self .metadata .get ("sip_status" )
111120
121+ @property
122+ def sip_transfer_reason (self ) -> Optional [str ]:
123+ """Why a transfer failed, e.g. "STR_RINGING_TIMEOUT". Only set for
124+ ``transfer_sip_participant``."""
125+ return self .metadata .get ("sip_transfer_reason" )
126+
127+ @property
128+ def sip_transfer_id (self ) -> Optional [str ]:
129+ """The id of the failed transfer, for matching against SIP transfer logs."""
130+ return self .metadata .get ("sip_transfer_id" )
131+
112132 @classmethod
113133 def from_server_error (cls , err : ServerError ) -> "SipCallError" :
114134 return cls (err .code , err .message , status = err .status , metadata = err .metadata )
115135
116136 def __str__ (self ) -> str :
117137 code = self .metadata .get ("sip_status_code" )
118- if code is None :
138+ transfer_reason = self .metadata .get ("sip_transfer_reason" )
139+ if code is None and transfer_reason is None :
119140 return super ().__str__ ()
120141 # A clear, SIP-specific representation, including any extra metadata.
121- reason = self .metadata .get ("sip_status" )
122- result = f"SIP call failed: { code } "
123- if reason :
124- result += f" { reason } "
125- result += f" ({ self .code } )"
126- extra = {
127- k : v
128- for k , v in self .metadata .items ()
129- if k not in ("sip_status_code" , "sip_status" , "error_details" )
130- }
142+ parts = []
143+ if transfer_reason is not None :
144+ parts .append (transfer_reason )
145+ if code is not None :
146+ reason = self .metadata .get ("sip_status" )
147+ parts .append (f"{ code } { reason } " if reason else str (code ))
148+ what = "SIP transfer failed" if transfer_reason is not None else "SIP call failed"
149+ result = f"{ what } : { ', ' .join (parts )} ({ self .code } )"
150+ extra = {k : v for k , v in self .metadata .items () if k not in _SIP_META_KEYS }
131151 if extra :
132152 result += " [" + ", " .join (f"{ k } ={ v } " for k , v in extra .items ()) + "]"
133153 return result
0 commit comments