Skip to content

Error Types

All SDK exceptions extend VaultSandboxException, which is a RuntimeException. This means exceptions are unchecked and don’t need to be declared in method signatures.

VaultSandboxException (base - extends RuntimeException)
├── ApiException (has statusCode)
│ ├── InboxNotFoundException (404)
│ ├── EmailNotFoundException (404)
│ └── WebhookNotFoundException (404)
├── NetworkException
├── TimeoutException
├── DecryptionException
├── SignatureVerificationException
├── SseException
├── StrategyException
├── InboxAlreadyExistsException
└── InvalidImportDataException (has getErrors())

Base exception for all SDK errors.

public class VaultSandboxException extends RuntimeException {
public VaultSandboxException(String message)
public VaultSandboxException(String message, Throwable cause)
}

Use this for catch-all handling:

try {
// SDK operations
} catch (VaultSandboxException e) {
// Handle any SDK error
System.err.println("VaultSandbox error: " + e.getMessage());
}

Thrown when the API returns an error response.

public class ApiException extends VaultSandboxException {
public int getStatusCode()
}
PropertyTypeDescription
statusCodeintHTTP status code from the API
CodeMeaningTypical Cause
400Bad RequestInvalid parameters
401UnauthorizedInvalid API key
403ForbiddenInsufficient permissions
404Not FoundInbox or email doesn’t exist
409ConflictInbox already exists (duplicate email or KEM key)
429Too Many RequestsRate limited
500Internal Server ErrorServer-side issue
502Bad GatewayGateway issue
503Service UnavailableServer overloaded
CodeMessageCause
400clientKemPk is required when encryption is enabledServer requires encryption but no KEM key provided
409An inbox with the same client KEM public key already existsDuplicate KEM key (encrypted inbox)
409An inbox with this email address already existsDuplicate email address (plain inbox)
try {
client.createInbox();
} catch (ApiException e) {
switch (e.getStatusCode()) {
case 401:
throw new IllegalStateException("Invalid API key", e);
case 429:
// Rate limited - retry after delay
Thread.sleep(1000);
client.createInbox();
break;
case 500:
case 502:
case 503:
// Server error - may want to retry
System.err.println("Server error: " + e.getMessage());
break;
default:
throw e;
}
}

Thrown when an inbox doesn’t exist. Extends ApiException with status code 404.

public class InboxNotFoundException extends ApiException {
// Constructor sets message to "Inbox not found: {emailAddress}"
}
try {
client.deleteInbox("nonexistent@example.com");
} catch (InboxNotFoundException e) {
System.out.println("Inbox not found: " + e.getMessage());
// Inbox may have expired or been deleted
}

Thrown when an email doesn’t exist. Extends ApiException with status code 404.

public class EmailNotFoundException extends ApiException {
// Constructor sets message to "Email not found: {emailId}"
}
try {
Email email = inbox.getEmail("invalid-email-id");
} catch (EmailNotFoundException e) {
System.out.println("Email not found: " + e.getMessage());
// Email may have been deleted
}

Thrown when a webhook doesn’t exist. Extends ApiException with status code 404.

public class WebhookNotFoundException extends ApiException {
// Constructor sets message to "Webhook not found: {webhookId}"
}
try {
WebhookData webhook = inbox.getWebhook("whk_nonexistent");
} catch (WebhookNotFoundException e) {
System.out.println("Webhook not found: " + e.getMessage());
// Webhook may have been deleted
}

Thrown when importing an inbox that’s already registered with the client, or when creating an inbox with a duplicate email address or KEM public key.

public class InboxAlreadyExistsException extends VaultSandboxException {
// Constructor sets message to "Inbox already exists: {emailAddress}"
}
try {
client.importInbox(exportedInbox);
} catch (InboxAlreadyExistsException e) {
System.out.println("Already registered: " + e.getMessage());
// Use existing inbox instead
Inbox existing = client.getInbox(emailAddress);
}

When creating inboxes, conflicts can occur differently based on encryption mode:

try {
Inbox inbox = client.createInbox(
CreateInboxOptions.builder()
.emailAddress("test@example.com")
.build()
);
} catch (ApiException e) {
if (e.getStatusCode() == 409) {
// For encrypted inboxes: "An inbox with the same client KEM public key already exists"
// For plain inboxes: "An inbox with this email address already exists"
System.out.println("Conflict: " + e.getMessage());
}
}

Thrown when import data is invalid or corrupted.

public class InvalidImportDataException extends VaultSandboxException {
public List<String> getErrors()
}
MethodReturn TypeDescription
getErrors()List<String>List of validation errors
try {
client.importInboxFromFile(path);
} catch (InvalidImportDataException e) {
System.out.println("Import failed: " + e.getMessage());
for (String error : e.getErrors()) {
System.out.println(" - " + error);
}
}

Thrown on network connectivity issues.

public class NetworkException extends VaultSandboxException {
public NetworkException(String message)
public NetworkException(String message, Throwable cause)
}
try {
client.createInbox();
} catch (NetworkException e) {
System.err.println("Network error: " + e.getMessage());
// Check internet connection
// Retry with backoff
}

Thrown when waiting for email times out.

public class TimeoutException extends VaultSandboxException {
public TimeoutException(String message)
public TimeoutException(String message, Throwable cause)
}
try {
Email email = inbox.waitForEmail(Duration.ofSeconds(30));
} catch (TimeoutException e) {
System.out.println("Timeout: " + e.getMessage());
// Email not received within timeout
// Check if email was actually sent
// Consider increasing timeout
}

Thrown when email decryption fails.

public class DecryptionException extends VaultSandboxException {
public DecryptionException(String message)
public DecryptionException(String message, Throwable cause)
}
  • Inbox has expired and keys are no longer valid
  • Cryptographic keys are corrupted
  • Server-side encryption mismatch
  • Data corruption during transmission
try {
Email email = inbox.getEmail(emailId);
} catch (DecryptionException e) {
System.err.println("Decryption failed: " + e.getMessage());
// Inbox may have expired - create a new one
}

Thrown when email signature verification fails.

public class SignatureVerificationException extends VaultSandboxException {
public SignatureVerificationException() // "SIGNATURE VERIFICATION FAILED - Data may be tampered!"
public SignatureVerificationException(String message)
public SignatureVerificationException(String message, Throwable cause)
}
  • Email data was tampered with in transit
  • Server signature key mismatch
  • Data corruption
try {
Email email = inbox.getEmail(emailId);
} catch (SignatureVerificationException e) {
System.err.println("Security warning: " + e.getMessage());
// Data integrity compromised - do not trust this email
}

Thrown on SSE (Server-Sent Events) connection issues.

public class SseException extends VaultSandboxException {
public SseException(String message)
public SseException(String message, Throwable cause)
}
  • SSE connection failed after max reconnect attempts
  • Firewall blocking SSE connections
  • Proxy not supporting SSE
  • Network instability
try {
Email email = inbox.waitForEmail(Duration.ofSeconds(30));
} catch (SseException e) {
System.err.println("SSE failed: " + e.getMessage());
// Consider using POLLING strategy instead
}

Thrown when the delivery strategy fails.

public class StrategyException extends VaultSandboxException {
public StrategyException(String message)
public StrategyException(String message, Throwable cause)
}
try {
Email email = inbox.waitForEmail(Duration.ofSeconds(30));
} catch (StrategyException e) {
System.err.println("Strategy error: " + e.getMessage());
}

Handle different error types appropriately:

try {
Email email = inbox.waitForEmail(Duration.ofSeconds(30));
processEmail(email);
} catch (TimeoutException e) {
// Handle timeout specifically
fail("Email not received within timeout");
} catch (SseException e) {
// SSE connection issues
System.err.println("Connection issue: " + e.getMessage());
} catch (ApiException e) {
// Handle API errors by status code
System.err.println("API error " + e.getStatusCode() + ": " + e.getMessage());
} catch (VaultSandboxException e) {
// Catch-all for other SDK errors
System.err.println("Error: " + e.getMessage());
}

Implement retries with exponential backoff:

public Email waitWithRetry(Inbox inbox, int maxAttempts) {
Exception lastException = null;
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return inbox.waitForEmail(Duration.ofSeconds(10));
} catch (TimeoutException e) {
lastException = e;
System.out.println("Attempt " + attempt + " timed out");
// Continue to next attempt
} catch (NetworkException e) {
lastException = e;
System.out.println("Attempt " + attempt + " network error: " + e.getMessage());
// Backoff before retry
try {
Thread.sleep(1000L * attempt);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new VaultSandboxException("Interrupted", ie);
}
}
}
throw new VaultSandboxException("Failed after " + maxAttempts + " attempts", lastException);
}

Return null or default values for non-critical errors:

public Email getEmailSafely(Inbox inbox, String emailId) {
try {
return inbox.getEmail(emailId);
} catch (EmailNotFoundException e) {
// Email was deleted
return null;
} catch (DecryptionException e) {
logger.warn("Decryption failed for {}: {}", emailId, e.getMessage());
return null;
}
}

Log errors with appropriate context:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(MyClass.class);
try {
Inbox inbox = client.createInbox();
} catch (ApiException e) {
logger.error("API error creating inbox: status={}, message={}",
e.getStatusCode(), e.getMessage(), e);
throw e;
} catch (NetworkException e) {
logger.error("Network error creating inbox: {}", e.getMessage(), e);
throw e;
} catch (VaultSandboxException e) {
logger.error("Error creating inbox: {}", e.getMessage(), e);
throw e;
}

Always clean up resources in finally blocks:

ClientConfig config = ClientConfig.builder()
.apiKey(apiKey)
.baseUrl(baseUrl)
.build();
VaultSandboxClient client = VaultSandboxClient.create(config);
Inbox inbox = null;
try {
inbox = client.createInbox();
Email email = inbox.waitForEmail(Duration.ofSeconds(30));
processEmail(email);
} catch (TimeoutException e) {
// Handle timeout
} finally {
if (inbox != null) {
try {
client.deleteInbox(inbox.getEmailAddress());
} catch (Exception e) {
logger.warn("Failed to cleanup inbox", e);
}
}
client.close();
}

Or use try-with-resources:

ClientConfig config = ClientConfig.builder()
.apiKey(apiKey)
.baseUrl(baseUrl)
.build();
try (VaultSandboxClient client = VaultSandboxClient.create(config)) {
Inbox inbox = client.createInbox();
try {
Email email = inbox.waitForEmail(Duration.ofSeconds(30));
processEmail(email);
} finally {
client.deleteInbox(inbox.getEmailAddress());
}
}

Use assertions for expected exceptions:

import static org.junit.jupiter.api.Assertions.*;
@Test
void testInboxNotFound() {
assertThrows(InboxNotFoundException.class, () -> {
client.deleteInbox("nonexistent@example.com");
});
}
@Test
void testTimeout() {
Inbox inbox = client.createInbox();
TimeoutException e = assertThrows(TimeoutException.class, () -> {
inbox.waitForEmail(Duration.ofMillis(100));
});
assertTrue(e.getMessage().contains("timeout"));
}
@Test
void testApiErrorStatusCode() {
ApiException e = assertThrows(ApiException.class, () -> {
// Operation that causes API error
});
assertEquals(404, e.getStatusCode());
}
  1. Be Specific - Catch specific exceptions when you need different handling for each type

  2. Don’t Swallow - Always log or rethrow exceptions; never silently ignore them

  3. Use Finally/Try-with-Resources - Clean up resources properly even when exceptions occur

  4. Set Appropriate Timeouts - Configure timeouts based on your requirements to fail fast

  5. Retry Smartly - Use exponential backoff for transient failures (network, rate limits)

  6. Test Error Paths - Write tests that verify error handling behavior

  7. Log Context - Include relevant information (IDs, status codes) in log messages

  8. Fail Fast - Don’t catch exceptions too broadly if you can’t handle them meaningfully

  • All exceptions are unchecked (extend RuntimeException)
  • Use multi-catch for similar handling: catch (NetworkException | TimeoutException e)
  • Use pattern matching (Java 16+): if (e instanceof ApiException api)
  • Integrate with SLF4J for logging