Fix unregistered-remote-error-type relay crash
Make `RemoteActorError` resilient to unresolved custom error types so that errors from remote actors always relay back to the caller - even when the user hasn't called `reg_err_types()` to register the exc type. Deats, - `.src_type`: log warning + return `None` instead of raising `TypeError` which was crashing the entire `_deliver_msg()` -> `pformat()` chain before the error could be relayed. - `.boxed_type_str`: fallback to `_ipc_msg.boxed_type_str` when the type obj can't be resolved so the type *name* is always available. - `unwrap_src_err()`: fallback to `RuntimeError` preserving original type name + traceback. - `unpack_error()`: log warning when `get_err_type()` returns `None` telling the user to call `reg_err_types()`. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-codemulticast_revertable_streams
parent
9c37b3f956
commit
a41c6d5c70
|
|
@ -434,24 +434,36 @@ class RemoteActorError(Exception):
|
||||||
return self._ipc_msg.src_type_str
|
return self._ipc_msg.src_type_str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def src_type(self) -> str:
|
def src_type(self) -> Type[BaseException]|None:
|
||||||
'''
|
'''
|
||||||
Error type raised by original remote faulting actor.
|
Error type raised by original remote faulting
|
||||||
|
actor.
|
||||||
|
|
||||||
When the error has only been relayed a single actor-hop
|
When the error has only been relayed a single
|
||||||
this will be the same as the `.boxed_type`.
|
actor-hop this will be the same as
|
||||||
|
`.boxed_type`.
|
||||||
|
|
||||||
|
If the type can not be resolved locally (i.e.
|
||||||
|
it was not registered via `reg_err_types()`)
|
||||||
|
a warning is logged and `None` is returned;
|
||||||
|
all string-level error info (`.src_type_str`,
|
||||||
|
`.tb_str`, etc.) remains available.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
if self._src_type is None:
|
if self._src_type is None:
|
||||||
self._src_type = get_err_type(
|
self._src_type = get_err_type(
|
||||||
self._ipc_msg.src_type_str
|
self._ipc_msg.src_type_str
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self._src_type:
|
if not self._src_type:
|
||||||
raise TypeError(
|
log.warning(
|
||||||
f'Failed to lookup src error type with '
|
f'Failed to lookup src error type via\n'
|
||||||
f'`tractor._exceptions.get_err_type()`:\n'
|
f'`tractor._exceptions.get_err_type()`:\n'
|
||||||
f'{self.src_type_str}'
|
f'\n'
|
||||||
|
f'`{self._ipc_msg.src_type_str}`'
|
||||||
|
f' is not registered!\n'
|
||||||
|
f'\n'
|
||||||
|
f'Call `reg_err_types()` to enable'
|
||||||
|
f' full type reconstruction.\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
return self._src_type
|
return self._src_type
|
||||||
|
|
@ -459,16 +471,26 @@ class RemoteActorError(Exception):
|
||||||
@property
|
@property
|
||||||
def boxed_type_str(self) -> str:
|
def boxed_type_str(self) -> str:
|
||||||
'''
|
'''
|
||||||
String-name of the (last hop's) boxed error type.
|
String-name of the (last hop's) boxed error
|
||||||
|
type.
|
||||||
|
|
||||||
|
Falls back to the IPC-msg-encoded type-name
|
||||||
|
str when the type can not be resolved locally
|
||||||
|
(e.g. unregistered custom errors).
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# TODO, maybe support also serializing the
|
# TODO, maybe support also serializing the
|
||||||
# `ExceptionGroup.exeptions: list[BaseException]` set under
|
# `ExceptionGroup.exeptions: list[BaseException]`
|
||||||
# certain conditions?
|
# set under certain conditions?
|
||||||
bt: Type[BaseException] = self.boxed_type
|
bt: Type[BaseException] = self.boxed_type
|
||||||
if bt:
|
if bt:
|
||||||
return str(bt.__name__)
|
return str(bt.__name__)
|
||||||
|
|
||||||
|
# fallback to the str name from the IPC msg
|
||||||
|
# when the type obj can't be resolved.
|
||||||
|
if self._ipc_msg:
|
||||||
|
return self._ipc_msg.boxed_type_str
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -701,10 +723,22 @@ class RemoteActorError(Exception):
|
||||||
failing actor's remote env.
|
failing actor's remote env.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# TODO: better tb insertion and all the fancier dunder
|
# TODO: better tb insertion and all the fancier
|
||||||
# metadata stuff as per `.__context__` etc. and friends:
|
# dunder metadata stuff as per `.__context__`
|
||||||
|
# etc. and friends:
|
||||||
# https://github.com/python-trio/trio/issues/611
|
# https://github.com/python-trio/trio/issues/611
|
||||||
src_type_ref: Type[BaseException] = self.src_type
|
src_type_ref: Type[BaseException]|None = (
|
||||||
|
self.src_type
|
||||||
|
)
|
||||||
|
if src_type_ref is None:
|
||||||
|
# unresolvable type: fall back to
|
||||||
|
# a `RuntimeError` preserving original
|
||||||
|
# traceback + type name.
|
||||||
|
return RuntimeError(
|
||||||
|
f'{self.src_type_str}: '
|
||||||
|
f'{self.tb_str}'
|
||||||
|
)
|
||||||
|
|
||||||
return src_type_ref(self.tb_str)
|
return src_type_ref(self.tb_str)
|
||||||
|
|
||||||
# TODO: local recontruction of nested inception for a given
|
# TODO: local recontruction of nested inception for a given
|
||||||
|
|
@ -1233,14 +1267,31 @@ def unpack_error(
|
||||||
if not isinstance(msg, Error):
|
if not isinstance(msg, Error):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# try to lookup a suitable error type from the local runtime
|
# try to lookup a suitable error type from the
|
||||||
# env then use it to construct a local instance.
|
# local runtime env then use it to construct a
|
||||||
# boxed_type_str: str = error_dict['boxed_type_str']
|
# local instance.
|
||||||
boxed_type_str: str = msg.boxed_type_str
|
boxed_type_str: str = msg.boxed_type_str
|
||||||
boxed_type: Type[BaseException] = get_err_type(boxed_type_str)
|
boxed_type: Type[BaseException] = get_err_type(
|
||||||
|
boxed_type_str
|
||||||
|
)
|
||||||
|
|
||||||
# retrieve the error's msg-encoded remotoe-env info
|
if boxed_type is None:
|
||||||
message: str = f'remote task raised a {msg.boxed_type_str!r}\n'
|
log.warning(
|
||||||
|
f'Failed to resolve remote error type\n'
|
||||||
|
f'`{boxed_type_str}` - boxing as\n'
|
||||||
|
f'`RemoteActorError` with original\n'
|
||||||
|
f'traceback preserved.\n'
|
||||||
|
f'\n'
|
||||||
|
f'Call `reg_err_types()` to enable\n'
|
||||||
|
f'full type reconstruction.\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
# retrieve the error's msg-encoded remote-env
|
||||||
|
# info
|
||||||
|
message: str = (
|
||||||
|
f'remote task raised a '
|
||||||
|
f'{msg.boxed_type_str!r}\n'
|
||||||
|
)
|
||||||
|
|
||||||
# TODO: do we even really need these checks for RAEs?
|
# TODO: do we even really need these checks for RAEs?
|
||||||
if boxed_type_str in [
|
if boxed_type_str in [
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue