Error Handling
The SDK provides a rich exception hierarchy for different error scenarios. Understand and handle errors gracefully in your applications.
Exception Hierarchy
All SDK exceptions inherit from FulfillmenttoolsException:
try {
Order order = client.orders().get(new OrderId("ord-invalid"));
} catch (FulfillmenttoolsException e) {
System.err.println("API error: " + e.getMessage());
}Specific Exceptions
NotFoundException
Thrown when a requested resource does not exist (HTTP 404).
try {
Order order = client.orders().get(new OrderId("nonexistent"));
} catch (NotFoundException e) {
System.out.println("Order not found: " + e.getMessage());
}ValidationException
Thrown when request validation fails (HTTP 422).
try {
Page<Order> page = client.orders().search(invalidRequest);
} catch (ValidationException e) {
System.out.println("Invalid request: " + e.getMessage());
}AuthenticationException
Thrown when authentication fails (HTTP 401).
try {
Order order = client.orders().get(new OrderId("ord-123"));
} catch (AuthenticationException e) {
System.out.println("Authentication failed. Refresh credentials.");
}ConflictException
Thrown when the request conflicts with the current state (HTTP 409).
try {
// Attempt operation that violates a constraint
} catch (ConflictException e) {
System.out.println("Conflict: " + e.getMessage());
}RateLimitException
Thrown when rate limits are exceeded (HTTP 429).
try {
Order order = client.orders().get(new OrderId("ord-123"));
} catch (RateLimitException e) {
if (e.retryAfter().isPresent()) {
System.out.println("Rate limited. Retry after: " + e.retryAfter().get());
} else {
System.out.println("Rate limited");
}
}ServerException
Thrown for server-side errors (HTTP 5xx).
try {
Order order = client.orders().get(new OrderId("ord-123"));
} catch (ServerException e) {
System.out.println("Server error: " + e.getMessage());
}Async Error Handling
Handle errors in async operations using exceptionally() or whenComplete():
client.orders().getAsync(new OrderId("ord-123"))
.exceptionally(throwable -> {
if (throwable instanceof NotFoundException) {
System.out.println("Order not found");
} else if (throwable instanceof AuthenticationException) {
System.out.println("Auth failed");
}
return null;
})
.thenAccept(order -> {
if (order != null) {
System.out.println("Order: " + order.getOrderNumber());
}
});Or use handle() to process both success and error cases:
client.orders().getAsync(new OrderId("ord-123"))
.handle((order, throwable) -> {
if (throwable != null) {
System.err.println("Error: " + throwable.getMessage());
return null;
}
return order;
});Retry Logic
The SDK automatically retries transient failures (5xx errors, timeouts) up to the configured retryMaxAttempts. Configure retries when building the client:
FulfillmenttoolsClient client = FulfillmenttoolsClient.builder()
.baseUrl("...")
.credentials(...)
.retryMaxAttempts(5) // Default is 3
.build();For rate limiting, inspect the RateLimitException for retry guidance:
try {
Order order = client.orders().get(new OrderId("ord-123"));
} catch (RateLimitException e) {
if (e.retryAfter().isPresent()) {
Duration waitTime = e.retryAfter().get();
Thread.sleep(waitTime.toMillis());
// Retry the operation
}
}