@@ -74,6 +74,19 @@ def _error_attr(error: BaseException, name: str) -> Any:
7474 return None
7575
7676
77+ def _safe_str (value : object ) -> str :
78+ """str() on a plugin-supplied error can itself raise; never let that escape the handler.
79+
80+ An escape replaces the sanitized envelope with a raw HTTP 500, which the
81+ controller's preflight reads as fail-open — a plugin-reported failure would
82+ silently become a proceed.
83+ """
84+ try :
85+ return str (value )
86+ except Exception :
87+ return "<unrenderable error>"
88+
89+
7790def failure_category_of (error : BaseException ) -> Optional [str ]:
7891 """Return the error's failure_category only when it is a plain string."""
7992 category = _error_attr (error , "failure_category" )
@@ -231,7 +244,7 @@ async def _stream_response():
231244 + "\n "
232245 )
233246 except Exception as e :
234- logger .error (f"Failure streaming response: { e } " , exc_info = True )
247+ logger .error (f"Failure streaming response: { _safe_str ( e ) } " , exc_info = True )
235248 yield (
236249 InvokeResponse (
237250 usage = usage ,
@@ -240,7 +253,7 @@ async def _stream_response():
240253 filedata_meta .model_dump ()
241254 ),
242255 status_code = status_code_of (e ),
243- status_code_text = f"[{ e .__class__ .__name__ } ] { e } " ,
256+ status_code_text = f"[{ e .__class__ .__name__ } ] { _safe_str ( e ) } " ,
244257 failure_category = failure_category_of (e ),
245258 ).model_dump_json ()
246259 + "\n "
@@ -259,7 +272,8 @@ async def _stream_response():
259272 )
260273 except HTTPException as exc :
261274 logger .error (
262- f"HTTPException: { exc .detail } (status_code={ exc .status_code } )" , exc_info = True
275+ f"HTTPException: { _safe_str (exc .detail )} (status_code={ exc .status_code } )" ,
276+ exc_info = True ,
263277 )
264278 return InvokeResponse (
265279 usage = usage ,
@@ -268,13 +282,13 @@ async def _stream_response():
268282 status_code = exc .status_code ,
269283 status_code_text = exc .detail
270284 if isinstance (exc .detail , str )
271- else json .dumps (exc .detail , default = str ),
285+ else json .dumps (exc .detail , default = _safe_str ),
272286 failure_category = failure_category_of (exc ),
273287 file_data = request_dict .get ("file_data" , None ),
274288 )
275289 except UnstructuredIngestError as exc :
276290 logger .error (
277- f"UnstructuredIngestError: { exc } "
291+ f"UnstructuredIngestError: { _safe_str ( exc ) } "
278292 f"(status_code={ _error_attr (exc , 'status_code' )} )" ,
279293 exc_info = True ,
280294 )
@@ -283,18 +297,18 @@ async def _stream_response():
283297 message_channels = message_channels ,
284298 filedata_meta = filedata_meta_model .model_validate (filedata_meta .model_dump ()),
285299 status_code = status_code_of (exc ),
286- status_code_text = str (exc ),
300+ status_code_text = _safe_str (exc ),
287301 failure_category = failure_category_of (exc ),
288302 file_data = request_dict .get ("file_data" , None ),
289303 )
290304 except Exception as invoke_error :
291- logger .error (f"failed to invoke plugin: { invoke_error } " , exc_info = True )
305+ logger .error (f"failed to invoke plugin: { _safe_str ( invoke_error ) } " , exc_info = True )
292306 return InvokeResponse (
293307 usage = usage ,
294308 message_channels = message_channels ,
295309 filedata_meta = filedata_meta_model .model_validate (filedata_meta .model_dump ()),
296310 status_code = status_code_of (invoke_error ),
297- status_code_text = f"[{ invoke_error .__class__ .__name__ } ] { invoke_error } " ,
311+ status_code_text = f"[{ invoke_error .__class__ .__name__ } ] { _safe_str ( invoke_error ) } " ,
298312 failure_category = failure_category_of (invoke_error ),
299313 file_data = request_dict .get ("file_data" , None ),
300314 )
0 commit comments