0
# Exception Hierarchy and Error Handling
1
2
Comprehensive exception hierarchy for handling workflow and activity failures, with support for retries, timeouts, cancellation, and custom application failures.
3
4
## Capabilities
5
6
### Base Exception Classes
7
8
Foundation classes for the Temporal exception hierarchy.
9
10
```java { .api }
11
/**
12
* Base class for all Temporal-related exceptions.
13
*/
14
public class TemporalException extends RuntimeException {
15
/**
16
* Creates TemporalException with message.
17
* @param message exception message
18
*/
19
public TemporalException(String message);
20
21
/**
22
* Creates TemporalException with message and cause.
23
* @param message exception message
24
* @param cause underlying cause
25
*/
26
public TemporalException(String message, Throwable cause);
27
28
/**
29
* Creates TemporalException with cause.
30
* @param cause underlying cause
31
*/
32
public TemporalException(Throwable cause);
33
}
34
35
/**
36
* Base for failures that can cross workflow boundaries.
37
*/
38
public abstract class TemporalFailure extends TemporalException {
39
/**
40
* Creates TemporalFailure with message and cause.
41
* @param message failure message
42
* @param cause original cause
43
* @param dataConverter data converter for details
44
*/
45
protected TemporalFailure(String message, Throwable cause, DataConverter dataConverter);
46
47
/**
48
* Gets original failure that was converted.
49
* @return original failure or null
50
*/
51
public Throwable getOriginalFailure();
52
53
/**
54
* Gets failure details as Optional.
55
* @param detailsClass class of details
56
* @param detailsType generic type of details
57
* @return optional failure details
58
*/
59
public <T> Optional<T> getDetails(Class<T> detailsClass, Type detailsType);
60
61
/**
62
* Gets failure details as Optional.
63
* @param detailsClass class of details
64
* @return optional failure details
65
*/
66
public <T> Optional<T> getDetails(Class<T> detailsClass);
67
68
/**
69
* Gets raw failure details.
70
* @return optional payloads
71
*/
72
public Optional<Payloads> getDetailsPayloads();
73
74
/**
75
* Gets data converter used for details.
76
* @return data converter
77
*/
78
public DataConverter getDataConverter();
79
}
80
```
81
82
### Application Failures
83
84
Application-level failures with custom error types and retry control.
85
86
```java { .api }
87
/**
88
* Application-level failures with custom error types.
89
*/
90
public final class ApplicationFailure extends TemporalFailure {
91
/**
92
* Creates new application failure.
93
* @param message error message
94
* @param type error type
95
* @param nonRetryable whether failure should not be retried
96
* @param details additional details
97
* @return ApplicationFailure instance
98
*/
99
public static ApplicationFailure newFailure(String message, String type, boolean nonRetryable, Object... details);
100
101
/**
102
* Creates retryable application failure.
103
* @param message error message
104
* @param type error type
105
* @param details additional details
106
* @return ApplicationFailure instance
107
*/
108
public static ApplicationFailure newFailure(String message, String type, Object... details);
109
110
/**
111
* Creates non-retryable application failure.
112
* @param message error message
113
* @param type error type
114
* @param details additional details
115
* @return ApplicationFailure instance
116
*/
117
public static ApplicationFailure newNonRetryableFailure(String message, String type, Object... details);
118
119
/**
120
* Creates application failure from throwable.
121
* @param message error message
122
* @param type error type
123
* @param nonRetryable whether failure should not be retried
124
* @param cause original cause
125
* @param details additional details
126
* @return ApplicationFailure instance
127
*/
128
public static ApplicationFailure newFailureWithCause(String message, String type, boolean nonRetryable, Throwable cause, Object... details);
129
130
/**
131
* Creates retryable failure with cause.
132
* @param message error message
133
* @param type error type
134
* @param cause original cause
135
* @param details additional details
136
* @return ApplicationFailure instance
137
*/
138
public static ApplicationFailure newFailureWithCause(String message, String type, Throwable cause, Object... details);
139
140
/**
141
* Creates non-retryable failure with cause.
142
* @param message error message
143
* @param type error type
144
* @param cause original cause
145
* @param details additional details
146
* @return ApplicationFailure instance
147
*/
148
public static ApplicationFailure newNonRetryableFailureWithCause(String message, String type, Throwable cause, Object... details);
149
150
/**
151
* Gets application error type.
152
* @return error type
153
*/
154
public String getType();
155
156
/**
157
* Checks if failure is non-retryable.
158
* @return true if non-retryable
159
*/
160
public boolean isNonRetryable();
161
}
162
```
163
164
**Usage Examples:**
165
166
```java
167
public class PaymentActivitiesImpl implements PaymentActivities {
168
@Override
169
public PaymentResult processPayment(PaymentInfo paymentInfo) {
170
try {
171
return paymentService.charge(paymentInfo);
172
} catch (InsufficientFundsException e) {
173
// Non-retryable business error
174
throw ApplicationFailure.newNonRetryableFailure(
175
"Insufficient funds",
176
"INSUFFICIENT_FUNDS",
177
paymentInfo.getAmount(),
178
paymentInfo.getAccountId()
179
);
180
} catch (PaymentGatewayException e) {
181
// Retryable technical error
182
throw ApplicationFailure.newFailure(
183
"Payment gateway temporarily unavailable",
184
"GATEWAY_ERROR",
185
e.getGatewayCode()
186
);
187
}
188
}
189
}
190
191
// Catching application failures in workflows
192
public class OrderWorkflowImpl implements OrderWorkflow {
193
@Override
194
public OrderResult processOrder(OrderRequest request) {
195
try {
196
PaymentResult payment = activities.processPayment(request.getPaymentInfo());
197
return new OrderResult(true, payment.getTransactionId());
198
} catch (ApplicationFailure e) {
199
if ("INSUFFICIENT_FUNDS".equals(e.getType())) {
200
// Handle insufficient funds
201
return new OrderResult(false, "Payment declined: insufficient funds");
202
} else if (e.isNonRetryable()) {
203
// Handle other non-retryable errors
204
return new OrderResult(false, "Payment failed: " + e.getMessage());
205
} else {
206
// Let retryable errors propagate
207
throw e;
208
}
209
}
210
}
211
}
212
```
213
214
### Activity Failures
215
216
Failures that wrap exceptions from activity executions.
217
218
```java { .api }
219
/**
220
* Wraps exceptions thrown by activity executions.
221
*/
222
public final class ActivityFailure extends TemporalFailure {
223
/**
224
* Gets scheduled event ID.
225
* @return scheduled event ID
226
*/
227
public long getScheduledEventId();
228
229
/**
230
* Gets started event ID.
231
* @return started event ID
232
*/
233
public long getStartedEventId();
234
235
/**
236
* Gets activity type.
237
* @return activity type
238
*/
239
public String getActivityType();
240
241
/**
242
* Gets activity ID.
243
* @return activity ID
244
*/
245
public String getActivityId();
246
247
/**
248
* Gets retry state.
249
* @return retry state
250
*/
251
public RetryState getRetryState();
252
}
253
```
254
255
### Child Workflow Failures
256
257
Failures from child workflow executions.
258
259
```java { .api }
260
/**
261
* Wraps failures from child workflow executions.
262
*/
263
public final class ChildWorkflowFailure extends TemporalFailure {
264
/**
265
* Gets namespace of child workflow.
266
* @return namespace
267
*/
268
public String getNamespace();
269
270
/**
271
* Gets workflow execution of child.
272
* @return workflow execution
273
*/
274
public WorkflowExecution getExecution();
275
276
/**
277
* Gets workflow type of child.
278
* @return workflow type
279
*/
280
public String getWorkflowType();
281
282
/**
283
* Gets initiated event ID.
284
* @return initiated event ID
285
*/
286
public long getInitiatedEventId();
287
288
/**
289
* Gets started event ID.
290
* @return started event ID
291
*/
292
public long getStartedEventId();
293
294
/**
295
* Gets retry state.
296
* @return retry state
297
*/
298
public RetryState getRetryState();
299
}
300
```
301
302
### Timeout Failures
303
304
Failures indicating timeout occurred during execution.
305
306
```java { .api }
307
/**
308
* Indicates timeout occurred during execution.
309
*/
310
public final class TimeoutFailure extends TemporalFailure {
311
/**
312
* Gets timeout type.
313
* @return timeout type
314
*/
315
public TimeoutType getTimeoutType();
316
317
/**
318
* Gets last heartbeat details if available.
319
* @param detailsClass class of details
320
* @param detailsType generic type of details
321
* @return optional heartbeat details
322
*/
323
public <T> Optional<T> getLastHeartbeatDetails(Class<T> detailsClass, Type detailsType);
324
325
/**
326
* Gets last heartbeat details if available.
327
* @param detailsClass class of details
328
* @return optional heartbeat details
329
*/
330
public <T> Optional<T> getLastHeartbeatDetails(Class<T> detailsClass);
331
}
332
333
/**
334
* Types of timeouts that can occur.
335
*/
336
public enum TimeoutType {
337
START_TO_CLOSE,
338
SCHEDULE_TO_START,
339
SCHEDULE_TO_CLOSE,
340
HEARTBEAT
341
}
342
```
343
344
**Usage Examples:**
345
346
```java
347
public class TimeoutHandlingWorkflowImpl implements TimeoutHandlingWorkflow {
348
@Override
349
public String processWithTimeouts(ProcessingRequest request) {
350
try {
351
// Activity with timeout
352
ProcessingActivities activities = Workflow.newActivityStub(
353
ProcessingActivities.class,
354
ActivityOptions.newBuilder()
355
.setStartToCloseTimeout(Duration.ofMinutes(5))
356
.setHeartbeatTimeout(Duration.ofMinutes(1))
357
.build()
358
);
359
360
return activities.processData(request);
361
} catch (ActivityFailure e) {
362
if (e.getCause() instanceof TimeoutFailure) {
363
TimeoutFailure timeout = (TimeoutFailure) e.getCause();
364
switch (timeout.getTimeoutType()) {
365
case START_TO_CLOSE:
366
return handleStartToCloseTimeout(request, timeout);
367
case HEARTBEAT:
368
return handleHeartbeatTimeout(request, timeout);
369
default:
370
return "Processing failed due to timeout: " + timeout.getTimeoutType();
371
}
372
}
373
throw e;
374
}
375
}
376
377
private String handleHeartbeatTimeout(ProcessingRequest request, TimeoutFailure timeout) {
378
// Get last heartbeat details to resume processing
379
Optional<ProcessingProgress> progress = timeout.getLastHeartbeatDetails(ProcessingProgress.class);
380
if (progress.isPresent()) {
381
// Resume from last known progress
382
request.setResumeFromIndex(progress.get().getProcessedCount());
383
return "Resuming processing from index: " + progress.get().getProcessedCount();
384
}
385
return "Processing timed out without progress information";
386
}
387
}
388
```
389
390
### Cancellation Failures
391
392
Failures indicating workflow or activity was canceled.
393
394
```java { .api }
395
/**
396
* Indicates workflow or activity was canceled.
397
*/
398
public final class CanceledFailure extends TemporalFailure {
399
/**
400
* Creates CanceledFailure with details.
401
* @param details cancellation details
402
* @param dataConverter data converter
403
* @return CanceledFailure instance
404
*/
405
public static CanceledFailure newFailure(Object details, DataConverter dataConverter);
406
407
/**
408
* Creates CanceledFailure with message and details.
409
* @param message cancellation message
410
* @param details cancellation details
411
* @param dataConverter data converter
412
* @return CanceledFailure instance
413
*/
414
public static CanceledFailure newFailure(String message, Object details, DataConverter dataConverter);
415
}
416
```
417
418
**Usage Examples:**
419
420
```java
421
public class CancellationHandlingWorkflowImpl implements CancellationHandlingWorkflow {
422
@Override
423
public String processWithCancellation(ProcessingRequest request) {
424
try {
425
// Long running activity
426
ProcessingActivities activities = Workflow.newActivityStub(ProcessingActivities.class);
427
return activities.processLargeDataset(request);
428
} catch (CanceledFailure e) {
429
// Handle cancellation gracefully
430
Optional<String> cancellationReason = e.getDetails(String.class);
431
String reason = cancellationReason.orElse("No reason provided");
432
433
// Perform cleanup
434
activities.cleanupPartialProcessing(request.getId());
435
436
return "Processing was canceled: " + reason;
437
}
438
}
439
}
440
441
public class CancellableActivityImpl implements CancellableActivity {
442
@Override
443
public String processData(String data) {
444
ActivityExecutionContext context = Activity.getExecutionContext();
445
446
try {
447
for (int i = 0; i < 1000; i++) {
448
// Check for cancellation via heartbeat
449
context.heartbeat("Processing item " + i);
450
451
// Process item
452
Thread.sleep(1000);
453
}
454
return "Processing completed";
455
} catch (ActivityCompletionException e) {
456
if (e.getCause() instanceof CanceledFailure) {
457
// Activity was canceled
458
return "Processing canceled at item " + getCurrentIndex();
459
}
460
throw Activity.wrap(e);
461
}
462
}
463
}
464
```
465
466
### Termination Failures
467
468
Failures indicating workflow was terminated.
469
470
```java { .api }
471
/**
472
* Indicates workflow was terminated.
473
*/
474
public final class TerminatedFailure extends TemporalFailure {
475
/**
476
* Creates TerminatedFailure.
477
* @param reason termination reason
478
* @param details termination details
479
* @param dataConverter data converter
480
* @return TerminatedFailure instance
481
*/
482
public static TerminatedFailure newFailure(String reason, Object details, DataConverter dataConverter);
483
}
484
```
485
486
### Server Failures
487
488
Failures originating from the Temporal server.
489
490
```java { .api }
491
/**
492
* Failures originating from the Temporal server.
493
*/
494
public final class ServerFailure extends TemporalFailure {
495
/**
496
* Gets server error type.
497
* @return server error type
498
*/
499
public String getType();
500
501
/**
502
* Checks if failure is non-retryable.
503
* @return true if non-retryable
504
*/
505
public boolean isNonRetryable();
506
}
507
```
508
509
### Nexus Operation Failures
510
511
Failures from Nexus operation executions.
512
513
```java { .api }
514
/**
515
* Failures from Nexus operation executions.
516
*/
517
public final class NexusOperationFailure extends TemporalFailure {
518
/**
519
* Gets scheduled event ID.
520
* @return scheduled event ID
521
*/
522
public long getScheduledEventId();
523
524
/**
525
* Gets endpoint name.
526
* @return endpoint name
527
*/
528
public String getEndpoint();
529
530
/**
531
* Gets service name.
532
* @return service name
533
*/
534
public String getService();
535
536
/**
537
* Gets operation name.
538
* @return operation name
539
*/
540
public String getOperation();
541
542
/**
543
* Gets operation ID.
544
* @return operation ID
545
*/
546
public String getOperationId();
547
}
548
```
549
550
### Default Failure Converter
551
552
Default implementation for converting between exceptions and Temporal failures.
553
554
```java { .api }
555
/**
556
* Default implementation for converting between exceptions and Temporal failures.
557
*/
558
public class DefaultFailureConverter implements FailureConverter {
559
/**
560
* Gets default instance.
561
* @return default failure converter
562
*/
563
public static DefaultFailureConverter INSTANCE;
564
565
/**
566
* Converts exception to failure.
567
* @param exception exception to convert
568
* @param dataConverter data converter for details
569
* @return temporal failure
570
*/
571
@Override
572
public TemporalFailure exceptionToFailure(Throwable exception, DataConverter dataConverter);
573
574
/**
575
* Converts failure to exception.
576
* @param failure failure to convert
577
* @param dataConverter data converter for details
578
* @return exception
579
*/
580
@Override
581
public Throwable failureToException(FailureInfo failure, DataConverter dataConverter);
582
}
583
584
/**
585
* Interface for converting between exceptions and failures.
586
*/
587
public interface FailureConverter {
588
/**
589
* Converts exception to failure for transmission.
590
* @param exception exception to convert
591
* @param dataConverter data converter
592
* @return temporal failure
593
*/
594
TemporalFailure exceptionToFailure(Throwable exception, DataConverter dataConverter);
595
596
/**
597
* Converts failure back to exception.
598
* @param failure failure info from server
599
* @param dataConverter data converter
600
* @return exception
601
*/
602
Throwable failureToException(FailureInfo failure, DataConverter dataConverter);
603
}
604
```
605
606
**Usage Examples:**
607
608
```java
609
public class CustomFailureConverter implements FailureConverter {
610
@Override
611
public TemporalFailure exceptionToFailure(Throwable exception, DataConverter dataConverter) {
612
if (exception instanceof CustomBusinessException) {
613
CustomBusinessException cbe = (CustomBusinessException) exception;
614
return ApplicationFailure.newFailure(
615
cbe.getMessage(),
616
cbe.getErrorCode(),
617
cbe.isRetryable() == false, // nonRetryable
618
cbe.getContext()
619
);
620
}
621
622
// Delegate to default converter
623
return DefaultFailureConverter.INSTANCE.exceptionToFailure(exception, dataConverter);
624
}
625
626
@Override
627
public Throwable failureToException(FailureInfo failure, DataConverter dataConverter) {
628
// Convert back to custom exception if needed
629
if (failure.getType().equals("CustomBusinessException")) {
630
return new CustomBusinessException(
631
failure.getMessage(),
632
failure.getSource(),
633
failure.getNonRetryableFlag()
634
);
635
}
636
637
// Delegate to default converter
638
return DefaultFailureConverter.INSTANCE.failureToException(failure, dataConverter);
639
}
640
}
641
642
public class ErrorHandlingExample {
643
public void demonstrateErrorHandling() {
644
try {
645
// Call activity that might fail
646
String result = activities.riskyOperation();
647
} catch (ActivityFailure activityFailure) {
648
Throwable cause = activityFailure.getCause();
649
650
if (cause instanceof ApplicationFailure) {
651
ApplicationFailure appFailure = (ApplicationFailure) cause;
652
653
switch (appFailure.getType()) {
654
case "VALIDATION_ERROR":
655
// Handle validation errors
656
handleValidationError(appFailure);
657
break;
658
case "BUSINESS_RULE_VIOLATION":
659
// Handle business rule violations
660
handleBusinessRuleViolation(appFailure);
661
break;
662
default:
663
// Handle other application failures
664
handleGenericApplicationFailure(appFailure);
665
}
666
} else if (cause instanceof TimeoutFailure) {
667
TimeoutFailure timeoutFailure = (TimeoutFailure) cause;
668
handleTimeout(timeoutFailure);
669
}
670
} catch (ChildWorkflowFailure childFailure) {
671
// Handle child workflow failures
672
System.err.println("Child workflow failed: " + childFailure.getWorkflowType());
673
}
674
}
675
676
private void handleValidationError(ApplicationFailure failure) {
677
Optional<ValidationErrorDetails> details = failure.getDetails(ValidationErrorDetails.class);
678
if (details.isPresent()) {
679
System.err.println("Validation failed: " + details.get().getFieldErrors());
680
}
681
}
682
}
683
```
684
685
### Retry State Enum
686
687
Indicates the current retry state of a failed execution.
688
689
```java { .api }
690
/**
691
* Indicates the current retry state of a failed execution.
692
*/
693
public enum RetryState {
694
/**
695
* Execution is being retried.
696
*/
697
IN_PROGRESS,
698
699
/**
700
* Execution failed non-retryably or retry policy exhausted.
701
*/
702
NON_RETRYABLE_FAILURE,
703
704
/**
705
* Timeout exceeded while retrying.
706
*/
707
TIMEOUT,
708
709
/**
710
* Maximum number of attempts reached.
711
*/
712
MAXIMUM_ATTEMPTS_REACHED,
713
714
/**
715
* Retry policy not set.
716
*/
717
RETRY_POLICY_NOT_SET,
718
719
/**
720
* Internal service error during retries.
721
*/
722
INTERNAL_SERVICE_ERROR,
723
724
/**
725
* Execution was canceled while retrying.
726
*/
727
CANCEL_REQUESTED
728
}
729
```