0
# Exception Handling
1
2
Comprehensive exception hierarchy for handling errors in Temporal workflows, activities, and client operations. All Temporal exceptions inherit from `TemporalError` and provide detailed error information for proper error handling and debugging.
3
4
## Capabilities
5
6
### Base Exception Classes
7
8
Foundation exception classes that provide common functionality for all Temporal errors.
9
10
```python { .api }
11
class TemporalError(Exception):
12
@property
13
def cause(self) -> Optional[BaseException]: ...
14
15
class FailureError(TemporalError):
16
def __init__(
17
self,
18
message: str,
19
*,
20
failure: Optional[temporalio.api.failure.v1.Failure] = None,
21
exc_args: Optional[Tuple] = None,
22
): ...
23
24
@property
25
def message(self) -> str: ...
26
27
@property
28
def failure(self) -> Optional[temporalio.api.failure.v1.Failure]: ...
29
```
30
31
### Application Errors
32
33
User-defined errors with configurable retry behavior, error categories, and custom details.
34
35
```python { .api }
36
class ApplicationErrorCategory(IntEnum):
37
UNSPECIFIED = 0
38
BENIGN = 1
39
40
class ApplicationError(FailureError):
41
def __init__(
42
self,
43
message: str,
44
*details: Any,
45
type: Optional[str] = None,
46
non_retryable: bool = False,
47
next_retry_delay: Optional[timedelta] = None,
48
category: ApplicationErrorCategory = ApplicationErrorCategory.UNSPECIFIED,
49
): ...
50
51
@property
52
def details(self) -> Sequence[Any]: ...
53
54
@property
55
def type(self) -> Optional[str]: ...
56
57
@property
58
def non_retryable(self) -> bool: ...
59
60
@property
61
def next_retry_delay(self) -> Optional[timedelta]: ...
62
63
@property
64
def category(self) -> ApplicationErrorCategory: ...
65
```
66
67
#### Usage Examples
68
69
```python
70
from temporalio.exceptions import ApplicationError, ApplicationErrorCategory
71
from datetime import timedelta
72
73
# Basic application error
74
raise ApplicationError("Invalid input data", type="ValidationError")
75
76
# Non-retryable error with custom category
77
raise ApplicationError(
78
"Authentication failed",
79
{"user_id": "123", "ip": "192.168.1.1"},
80
type="AuthError",
81
non_retryable=True,
82
category=ApplicationErrorCategory.BENIGN
83
)
84
85
# Retryable error with custom delay
86
raise ApplicationError(
87
"Rate limit exceeded",
88
type="RateLimitError",
89
next_retry_delay=timedelta(minutes=5)
90
)
91
```
92
93
### Workflow Lifecycle Errors
94
95
Errors related to workflow execution, cancellation, and termination.
96
97
```python { .api }
98
class CancelledError(FailureError):
99
def __init__(self, message: str = "Cancelled", *details: Any): ...
100
101
@property
102
def details(self) -> Sequence[Any]: ...
103
104
class TerminatedError(FailureError):
105
def __init__(self, message: str, *details: Any): ...
106
107
@property
108
def details(self) -> Sequence[Any]: ...
109
110
class WorkflowAlreadyStartedError(FailureError):
111
def __init__(
112
self, workflow_id: str, workflow_type: str, *, run_id: Optional[str] = None
113
): ...
114
115
workflow_id: str
116
workflow_type: str
117
run_id: Optional[str]
118
```
119
120
### Timeout Errors
121
122
Errors related to various types of timeouts in workflows and activities.
123
124
```python { .api }
125
class TimeoutType(IntEnum):
126
START_TO_CLOSE = 0
127
SCHEDULE_TO_START = 1
128
SCHEDULE_TO_CLOSE = 2
129
HEARTBEAT = 3
130
131
class TimeoutError(FailureError):
132
def __init__(
133
self,
134
message: str,
135
*,
136
type: Optional[TimeoutType],
137
last_heartbeat_details: Sequence[Any],
138
): ...
139
140
@property
141
def type(self) -> Optional[TimeoutType]: ...
142
143
@property
144
def last_heartbeat_details(self) -> Sequence[Any]: ...
145
```
146
147
#### Usage Examples
148
149
```python
150
from temporalio.exceptions import TimeoutError, TimeoutType, is_cancelled_exception
151
import temporalio.workflow as workflow
152
153
@workflow.defn
154
class MyWorkflow:
155
@workflow.run
156
async def run(self) -> str:
157
try:
158
result = await workflow.execute_activity(
159
my_activity,
160
"data",
161
schedule_to_close_timeout=timedelta(seconds=30)
162
)
163
return result
164
except TimeoutError as e:
165
if e.type == TimeoutType.SCHEDULE_TO_CLOSE:
166
# Handle activity timeout
167
return "Activity timed out"
168
except Exception as e:
169
if is_cancelled_exception(e):
170
# Handle cancellation
171
return "Workflow cancelled"
172
raise
173
```
174
175
### Activity Errors
176
177
Errors specific to activity execution, including retry information and activity metadata.
178
179
```python { .api }
180
class RetryState(IntEnum):
181
IN_PROGRESS = 0
182
NON_RETRYABLE_FAILURE = 1
183
TIMEOUT = 2
184
MAXIMUM_ATTEMPTS_REACHED = 3
185
RETRY_POLICY_NOT_SET = 4
186
INTERNAL_SERVER_ERROR = 5
187
CANCEL_REQUESTED = 6
188
189
class ActivityError(FailureError):
190
def __init__(
191
self,
192
message: str,
193
*,
194
scheduled_event_id: int,
195
started_event_id: int,
196
identity: str,
197
activity_type: str,
198
activity_id: str,
199
retry_state: Optional[RetryState],
200
): ...
201
202
@property
203
def scheduled_event_id(self) -> int: ...
204
205
@property
206
def started_event_id(self) -> int: ...
207
208
@property
209
def identity(self) -> str: ...
210
211
@property
212
def activity_type(self) -> str: ...
213
214
@property
215
def activity_id(self) -> str: ...
216
217
@property
218
def retry_state(self) -> Optional[RetryState]: ...
219
```
220
221
### Child Workflow Errors
222
223
Errors from child workflow execution with detailed workflow information.
224
225
```python { .api }
226
class ChildWorkflowError(FailureError):
227
def __init__(
228
self,
229
message: str,
230
*,
231
namespace: str,
232
workflow_id: str,
233
run_id: str,
234
workflow_type: str,
235
initiated_event_id: int,
236
started_event_id: int,
237
retry_state: Optional[RetryState],
238
): ...
239
240
@property
241
def namespace(self) -> str: ...
242
243
@property
244
def workflow_id(self) -> str: ...
245
246
@property
247
def run_id(self) -> str: ...
248
249
@property
250
def workflow_type(self) -> str: ...
251
252
@property
253
def initiated_event_id(self) -> int: ...
254
255
@property
256
def started_event_id(self) -> int: ...
257
258
@property
259
def retry_state(self) -> Optional[RetryState]: ...
260
```
261
262
### Client-Side Errors
263
264
Errors that occur during client operations and workflow management.
265
266
```python { .api }
267
class WorkflowFailureError(TemporalError):
268
pass
269
270
class WorkflowContinuedAsNewError(TemporalError):
271
pass
272
273
class WorkflowQueryRejectedError(TemporalError):
274
pass
275
276
class WorkflowQueryFailedError(TemporalError):
277
pass
278
279
class WorkflowUpdateFailedError(TemporalError):
280
pass
281
282
class RPCTimeoutOrCancelledError(TemporalError):
283
pass
284
285
class WorkflowUpdateRPCTimeoutOrCancelledError(RPCTimeoutOrCancelledError):
286
pass
287
288
class AsyncActivityCancelledError(TemporalError):
289
pass
290
291
class ScheduleAlreadyRunningError(TemporalError):
292
pass
293
```
294
295
### Service and RPC Errors
296
297
Errors from communication with Temporal server and RPC operations.
298
299
```python { .api }
300
class ServerError(FailureError):
301
def __init__(self, message: str, *, non_retryable: bool = False): ...
302
303
@property
304
def non_retryable(self) -> bool: ...
305
306
class RPCError(TemporalError):
307
pass
308
```
309
310
### Nexus Operation Errors
311
312
Errors from Nexus service operations with detailed operation metadata.
313
314
```python { .api }
315
class NexusOperationError(FailureError):
316
def __init__(
317
self,
318
message: str,
319
*,
320
scheduled_event_id: int,
321
endpoint: str,
322
service: str,
323
operation: str,
324
operation_token: str,
325
): ...
326
327
@property
328
def scheduled_event_id(self) -> int: ...
329
330
@property
331
def endpoint(self) -> str: ...
332
333
@property
334
def service(self) -> str: ...
335
336
@property
337
def operation(self) -> str: ...
338
339
@property
340
def operation_token(self) -> str: ...
341
```
342
343
### Workflow-Specific Errors
344
345
Errors that occur within workflow execution context.
346
347
```python { .api }
348
class ContinueAsNewError(BaseException):
349
pass
350
351
class NondeterminismError(TemporalError):
352
pass
353
354
class ReadOnlyContextError(TemporalError):
355
pass
356
357
class RestrictedWorkflowAccessError(NondeterminismError):
358
pass
359
```
360
361
### Utility Functions
362
363
Helper functions for error handling and classification.
364
365
```python { .api }
366
def is_cancelled_exception(exception: BaseException) -> bool:
367
"""Check whether the given exception is considered a cancellation exception
368
according to Temporal.
369
370
This is often used in a conditional of a catch clause to check whether a
371
cancel occurred inside of a workflow. This can occur from
372
asyncio.CancelledError or CancelledError or either ActivityError or
373
ChildWorkflowError if either of those latter two have a CancelledError cause.
374
375
Args:
376
exception: Exception to check.
377
378
Returns:
379
True if a cancelled exception, false if not.
380
"""
381
```
382
383
#### Usage Examples
384
385
```python
386
from temporalio.exceptions import (
387
is_cancelled_exception,
388
ActivityError,
389
ChildWorkflowError,
390
ApplicationError,
391
RetryState
392
)
393
import temporalio.workflow as workflow
394
395
@workflow.defn
396
class ErrorHandlingWorkflow:
397
@workflow.run
398
async def run(self) -> str:
399
try:
400
# Execute child workflow
401
result = await workflow.execute_child_workflow(
402
ChildWorkflow.run,
403
"data",
404
id="child-workflow"
405
)
406
return result
407
except ActivityError as e:
408
if e.retry_state == RetryState.MAXIMUM_ATTEMPTS_REACHED:
409
# Handle max retries reached
410
return f"Activity {e.activity_type} failed after max retries"
411
except ChildWorkflowError as e:
412
if e.retry_state == RetryState.NON_RETRYABLE_FAILURE:
413
# Handle non-retryable child workflow failure
414
return f"Child workflow {e.workflow_type} failed permanently"
415
except Exception as e:
416
if is_cancelled_exception(e):
417
# Handle any cancellation
418
return "Operation was cancelled"
419
# Re-raise unknown exceptions
420
raise
421
422
return "Success"
423
```
424
425
## Error Handling Best Practices
426
427
### Exception Hierarchies
428
429
All Temporal exceptions inherit from `TemporalError`, allowing for broad exception catching:
430
431
```python
432
try:
433
# Temporal operations
434
result = await client.execute_workflow(...)
435
except TemporalError as e:
436
# Handle any Temporal-related error
437
print(f"Temporal error: {e}")
438
except Exception as e:
439
# Handle non-Temporal errors
440
print(f"Other error: {e}")
441
```
442
443
### Specific Error Handling
444
445
Handle specific error types for precise error recovery:
446
447
```python
448
try:
449
result = await workflow.execute_activity(activity_func, data)
450
except TimeoutError as e:
451
if e.type == TimeoutType.HEARTBEAT:
452
# Handle heartbeat timeout specifically
453
pass
454
except ApplicationError as e:
455
if e.type == "ValidationError":
456
# Handle validation errors
457
pass
458
elif e.non_retryable:
459
# Handle non-retryable errors
460
pass
461
```
462
463
### Cancellation Detection
464
465
Use the utility function to detect cancellation across different error types:
466
467
```python
468
from temporalio.exceptions import is_cancelled_exception
469
470
try:
471
# Workflow operations
472
await workflow.execute_activity(...)
473
except Exception as e:
474
if is_cancelled_exception(e):
475
# Handle cancellation uniformly
476
return "Operation cancelled"
477
raise
478
```