0
# Exception Handling
1
2
The Fabric8 Kubernetes Client provides a comprehensive exception hierarchy for handling various error conditions that can occur during Kubernetes API operations. Understanding these exceptions is crucial for building robust applications.
3
4
## Core Exception Classes
5
6
### KubernetesClientException
7
8
Main exception class for all Kubernetes client operations.
9
10
```java { .api }
11
public class KubernetesClientException extends RuntimeException {
12
// Constructors
13
public KubernetesClientException(String message);
14
public KubernetesClientException(String message, Throwable cause);
15
public KubernetesClientException(Throwable cause);
16
public KubernetesClientException(Status status);
17
public KubernetesClientException(String message, int code, Status status);
18
19
// Status information
20
public Status getStatus();
21
public int getCode();
22
public String getFullResourceName();
23
24
// Utility methods
25
public static KubernetesClientException launderThrowable(Throwable cause);
26
public static KubernetesClientException launderThrowable(String message, Throwable cause);
27
}
28
```
29
30
### ResourceNotFoundException
31
32
Exception thrown when a requested resource is not found.
33
34
```java { .api }
35
public class ResourceNotFoundException extends KubernetesClientException {
36
public ResourceNotFoundException(String message);
37
public ResourceNotFoundException(String message, Throwable cause);
38
}
39
```
40
41
### KubernetesClientTimeoutException
42
43
Exception thrown when operations exceed configured timeout values.
44
45
```java { .api }
46
public class KubernetesClientTimeoutException extends KubernetesClientException {
47
public KubernetesClientTimeoutException(String message);
48
public KubernetesClientTimeoutException(String message, long timeout, TimeUnit timeUnit);
49
public KubernetesClientTimeoutException(String message, Throwable cause);
50
51
public long getTimeout();
52
public TimeUnit getTimeUnit();
53
}
54
```
55
56
### WatcherException
57
58
Exception specific to watch operations and informer functionality.
59
60
```java { .api }
61
public class WatcherException extends Exception {
62
public WatcherException(String message);
63
public WatcherException(String message, Throwable cause);
64
public WatcherException(Throwable cause);
65
66
// Conversion methods
67
public KubernetesClientException asClientException();
68
69
// HTTP Gone detection
70
public boolean isHttpGone();
71
}
72
```
73
74
## HTTP Status Code Mapping
75
76
The client maps HTTP status codes to specific exception conditions:
77
78
- **200-299**: Success (no exception)
79
- **400**: Bad Request - Invalid resource definition or parameters
80
- **401**: Unauthorized - Authentication required or failed
81
- **403**: Forbidden - Insufficient permissions for the operation
82
- **404**: Not Found - Resource or endpoint doesn't exist
83
- **409**: Conflict - Resource already exists or version conflict
84
- **410**: Gone - Resource version too old (common in watch operations)
85
- **422**: Unprocessable Entity - Validation errors
86
- **429**: Too Many Requests - Rate limiting
87
- **500**: Internal Server Error - Kubernetes API server error
88
- **503**: Service Unavailable - API server overloaded or maintenance
89
90
## Usage Examples
91
92
### Basic Exception Handling
93
94
```java
95
try {
96
Pod pod = client.pods().withName("my-pod").get();
97
if (pod != null) {
98
System.out.println("Pod found: " + pod.getMetadata().getName());
99
} else {
100
System.out.println("Pod not found (returned null)");
101
}
102
103
} catch (KubernetesClientException e) {
104
System.out.println("Error accessing pod: " + e.getMessage());
105
System.out.println("HTTP status code: " + e.getCode());
106
107
if (e.getStatus() != null) {
108
Status status = e.getStatus();
109
System.out.println("API status: " + status.getStatus());
110
System.out.println("Reason: " + status.getReason());
111
System.out.println("Message: " + status.getMessage());
112
}
113
}
114
```
115
116
### Handling Specific HTTP Status Codes
117
118
```java
119
try {
120
// Try to create a resource
121
ConfigMap configMap = client.configMaps().create(new ConfigMapBuilder()
122
.withNewMetadata()
123
.withName("my-config")
124
.endMetadata()
125
.withData(Map.of("key", "value"))
126
.build());
127
128
} catch (KubernetesClientException e) {
129
switch (e.getCode()) {
130
case 400:
131
System.out.println("Bad request - invalid resource definition: " + e.getMessage());
132
break;
133
case 401:
134
System.out.println("Authentication required: " + e.getMessage());
135
break;
136
case 403:
137
System.out.println("Access denied - insufficient permissions: " + e.getMessage());
138
break;
139
case 409:
140
System.out.println("Resource already exists: " + e.getMessage());
141
// Maybe try to update instead
142
break;
143
case 422:
144
System.out.println("Validation error: " + e.getMessage());
145
if (e.getStatus() != null && e.getStatus().getDetails() != null) {
146
StatusDetails details = e.getStatus().getDetails();
147
if (details.getCauses() != null) {
148
for (StatusCause cause : details.getCauses()) {
149
System.out.println(" Field: " + cause.getField() +
150
", Reason: " + cause.getReason() +
151
", Message: " + cause.getMessage());
152
}
153
}
154
}
155
break;
156
case 429:
157
System.out.println("Rate limited - too many requests: " + e.getMessage());
158
// Implement backoff and retry
159
break;
160
default:
161
System.out.println("Unexpected error (HTTP " + e.getCode() + "): " + e.getMessage());
162
}
163
}
164
```
165
166
### ResourceNotFoundException Handling
167
168
```java
169
// Using require() method that throws ResourceNotFoundException
170
try {
171
Pod pod = client.pods().withName("required-pod").require();
172
// Pod is guaranteed to exist here
173
System.out.println("Required pod found: " + pod.getMetadata().getName());
174
175
} catch (ResourceNotFoundException e) {
176
System.out.println("Required pod not found: " + e.getMessage());
177
// Handle missing resource
178
}
179
180
// Alternative approach with explicit null check
181
Pod pod = client.pods().withName("optional-pod").get();
182
if (pod == null) {
183
System.out.println("Optional pod not found");
184
} else {
185
System.out.println("Optional pod found: " + pod.getMetadata().getName());
186
}
187
```
188
189
### Timeout Exception Handling
190
191
```java
192
try {
193
// Wait for pod to be ready with timeout
194
Pod readyPod = client.pods().withName("slow-starting-pod")
195
.waitUntilReady(2, TimeUnit.MINUTES);
196
197
System.out.println("Pod is ready: " + readyPod.getMetadata().getName());
198
199
} catch (KubernetesClientTimeoutException e) {
200
System.out.println("Timeout waiting for pod to be ready: " + e.getMessage());
201
System.out.println("Timeout was: " + e.getTimeout() + " " + e.getTimeUnit());
202
203
// Check current pod status
204
Pod currentPod = client.pods().withName("slow-starting-pod").get();
205
if (currentPod != null) {
206
PodStatus status = currentPod.getStatus();
207
System.out.println("Current phase: " + status.getPhase());
208
209
if (status.getConditions() != null) {
210
for (PodCondition condition : status.getConditions()) {
211
System.out.println("Condition: " + condition.getType() +
212
" = " + condition.getStatus() +
213
" (" + condition.getReason() + ")");
214
}
215
}
216
}
217
218
} catch (InterruptedException e) {
219
Thread.currentThread().interrupt();
220
System.out.println("Wait interrupted: " + e.getMessage());
221
}
222
```
223
224
### Watch Exception Handling
225
226
```java
227
Watch watch = client.pods().watch(new Watcher<Pod>() {
228
@Override
229
public void eventReceived(Action action, Pod pod) {
230
try {
231
System.out.println(action + ": " + pod.getMetadata().getName());
232
// Process the event
233
processEvent(action, pod);
234
235
} catch (Exception e) {
236
System.err.println("Error processing event: " + e.getMessage());
237
}
238
}
239
240
@Override
241
public void onClose(WatcherException cause) {
242
if (cause != null) {
243
System.out.println("Watch closed with error: " + cause.getMessage());
244
245
if (cause.isHttpGone()) {
246
System.out.println("HTTP 410 Gone - resource version too old");
247
System.out.println("Need to restart watch with fresh resource version");
248
restartWatch();
249
} else {
250
// Other error - convert to client exception for detailed info
251
KubernetesClientException clientException = cause.asClientException();
252
System.out.println("Watch error code: " + clientException.getCode());
253
254
// Decide whether to restart based on error type
255
if (clientException.getCode() >= 500) {
256
System.out.println("Server error - will retry watch");
257
scheduleWatchRetry();
258
} else {
259
System.out.println("Client error - not retrying");
260
}
261
}
262
} else {
263
System.out.println("Watch closed normally");
264
}
265
}
266
});
267
```
268
269
### Exception Chaining and Root Cause Analysis
270
271
```java
272
try {
273
// Complex operation that might have nested exceptions
274
performComplexKubernetesOperation();
275
276
} catch (KubernetesClientException e) {
277
System.out.println("Kubernetes operation failed: " + e.getMessage());
278
279
// Analyze the exception chain
280
Throwable cause = e.getCause();
281
while (cause != null) {
282
System.out.println("Caused by: " + cause.getClass().getSimpleName() +
283
": " + cause.getMessage());
284
cause = cause.getCause();
285
}
286
287
// Use launderThrowable to clean up exception chain
288
KubernetesClientException laundered = KubernetesClientException.launderThrowable(e);
289
System.out.println("Laundered exception: " + laundered.getMessage());
290
}
291
292
private void performComplexKubernetesOperation() {
293
try {
294
// Some operation that might throw various exceptions
295
client.apps().deployments().withName("my-deployment").scale(5);
296
297
} catch (Exception e) {
298
// Wrap and re-throw with context
299
throw KubernetesClientException.launderThrowable("Failed to scale deployment", e);
300
}
301
}
302
```
303
304
### Retry Logic with Exponential Backoff
305
306
```java
307
public <T> T retryOperation(Supplier<T> operation, int maxRetries) {
308
int retries = 0;
309
long delay = 1000; // Start with 1 second
310
311
while (retries < maxRetries) {
312
try {
313
return operation.get();
314
315
} catch (KubernetesClientException e) {
316
retries++;
317
318
// Don't retry on client errors (4xx)
319
if (e.getCode() >= 400 && e.getCode() < 500 && e.getCode() != 429) {
320
throw e;
321
}
322
323
// Don't retry on the last attempt
324
if (retries >= maxRetries) {
325
throw e;
326
}
327
328
System.out.println("Operation failed (attempt " + retries + "/" + maxRetries +
329
"): " + e.getMessage() + ". Retrying in " + delay + "ms");
330
331
try {
332
Thread.sleep(delay);
333
} catch (InterruptedException ie) {
334
Thread.currentThread().interrupt();
335
throw new RuntimeException("Retry interrupted", ie);
336
}
337
338
// Exponential backoff with jitter
339
delay = Math.min(delay * 2 + (long)(Math.random() * 1000), 30000);
340
}
341
}
342
343
throw new RuntimeException("Max retries exceeded");
344
}
345
346
// Usage example
347
try {
348
Pod pod = retryOperation(() ->
349
client.pods().withName("unstable-pod").get(), 3);
350
351
if (pod != null) {
352
System.out.println("Successfully retrieved pod: " + pod.getMetadata().getName());
353
}
354
355
} catch (Exception e) {
356
System.out.println("Failed to retrieve pod after retries: " + e.getMessage());
357
}
358
```
359
360
### Validation Error Handling
361
362
```java
363
try {
364
// Create a pod with invalid configuration
365
Pod invalidPod = client.pods().create(new PodBuilder()
366
.withNewMetadata()
367
.withName("invalid-pod-name-with-underscores_here") // Invalid name
368
.endMetadata()
369
.withNewSpec()
370
.addNewContainer()
371
.withName("app")
372
.withImage("") // Invalid empty image
373
.endContainer()
374
.endSpec()
375
.build());
376
377
} catch (KubernetesClientException e) {
378
if (e.getCode() == 422) { // Unprocessable Entity
379
System.out.println("Validation errors occurred:");
380
381
Status status = e.getStatus();
382
if (status != null && status.getDetails() != null) {
383
StatusDetails details = status.getDetails();
384
385
System.out.println("Resource: " + details.getKind() + "/" + details.getName());
386
387
if (details.getCauses() != null) {
388
for (StatusCause cause : details.getCauses()) {
389
System.out.println(" Field '" + cause.getField() + "': " +
390
cause.getMessage() + " (reason: " + cause.getReason() + ")");
391
}
392
}
393
}
394
} else {
395
System.out.println("Other error: " + e.getMessage());
396
}
397
}
398
```
399
400
### Global Exception Handler
401
402
```java
403
public class KubernetesExceptionHandler {
404
405
public static void handleException(KubernetesClientException e, String operation) {
406
System.err.println("Kubernetes operation failed: " + operation);
407
System.err.println("Error: " + e.getMessage());
408
System.err.println("HTTP Code: " + e.getCode());
409
410
// Log full resource name if available
411
if (e.getFullResourceName() != null) {
412
System.err.println("Resource: " + e.getFullResourceName());
413
}
414
415
// Log Kubernetes status if available
416
if (e.getStatus() != null) {
417
Status status = e.getStatus();
418
System.err.println("Status: " + status.getStatus());
419
System.err.println("Reason: " + status.getReason());
420
421
if (status.getDetails() != null) {
422
StatusDetails details = status.getDetails();
423
System.err.println("Kind: " + details.getKind());
424
System.err.println("Name: " + details.getName());
425
System.err.println("Group: " + details.getGroup());
426
}
427
}
428
429
// Log stack trace for debugging
430
e.printStackTrace();
431
}
432
433
public static boolean isRetryable(KubernetesClientException e) {
434
int code = e.getCode();
435
436
// Retry on server errors and rate limiting
437
if (code >= 500 || code == 429) {
438
return true;
439
}
440
441
// Retry on network-related errors
442
if (e.getCause() instanceof java.net.SocketTimeoutException ||
443
e.getCause() instanceof java.net.ConnectException) {
444
return true;
445
}
446
447
return false;
448
}
449
}
450
451
// Usage in application
452
try {
453
Pod pod = client.pods().create(newPod);
454
} catch (KubernetesClientException e) {
455
KubernetesExceptionHandler.handleException(e, "create pod");
456
457
if (KubernetesExceptionHandler.isRetryable(e)) {
458
// Implement retry logic
459
scheduleRetry();
460
}
461
}
462
```