-
Notifications
You must be signed in to change notification settings - Fork 10
Description
Expected Behavior
When a custom exception is raised inside a ctx.parallel() branch, I'd expect error_type on the resulting CallableRuntimeError (from batch_result.throw_if_error()) to be my original exception's class name — the same way it works with ctx.run_in_child_context(), where error_type correctly preserves the original class name.
try:
result.throw_if_error()
except CallableRuntimeError as e:
print(e.error_type) # Expected: 'PermanentFailure'Actual Behavior
error_type always comes back as 'CallableRuntimeError' instead of my original exception type. It looks like the exception goes through ErrorObject.from_exception() twice:
First wrap (in ChildOperationExecutor.execute(), operation/child.py):
- My code raises
PermanentFailure("Invalid input data") ErrorObject.from_exception(e)→ErrorObject(type="PermanentFailure")— correctto_callable_runtime_error()→CallableRuntimeError(error_type="PermanentFailure")— correct
Second wrap (in ConcurrentExecutor._create_result(), concurrency/executor.py):
ErrorObject.from_exception(executable.error)is called on the already-wrappedCallableRuntimeErrortype(exception).__name__is nowCallableRuntimeError, so the originalerror_type="PermanentFailure"is lost
I'm wondering if I'm doing something wrong, or if this is a gap in how exceptions flow through parallel branches?
Steps to Reproduce
- Define a custom exception and a durable function that uses
ctx.parallel():
class PermanentFailure(Exception):
pass
def my_handler(ctx):
def branch1(child_ctx):
raise PermanentFailure("Invalid input data")
result = ctx.parallel([branch1])
result.throw_if_error()- Catch the
CallableRuntimeErrorand inspecterror_type:
try:
result.throw_if_error()
except CallableRuntimeError as e:
print(e.error_type) # Returns 'CallableRuntimeError', not 'PermanentFailure'- Compare with
ctx.run_in_child_context()which correctly preserveserror_type='PermanentFailure'(only one wrap).
SDK Version
1.3.0
Python Version
3.14
Is this a regression?
No
Last Working Version
No response
Additional Context
Possible fix? Would it make sense for ErrorObject.from_exception() to check if the exception is already a CallableRuntimeError and preserve its error_type?
@classmethod
def from_exception(cls, exception: Exception) -> ErrorObject:
if isinstance(exception, CallableRuntimeError) and exception.error_type:
return cls(
message=str(exception),
type=exception.error_type, # Preserve original type
data=exception.data,
stack_trace=exception.stack_trace,
)
return cls(
message=str(exception),
type=type(exception).__name__,
data=None,
stack_trace=None,
)Workaround: Using serial ctx.run_in_child_context() calls instead of ctx.parallel(), which only wraps once and preserves error_type.