Enum Class SdkErrorCode

java.lang.Object
java.lang.Enum<SdkErrorCode>
co.ankatech.ankasecure.sdk.exception.SdkErrorCode
All Implemented Interfaces:
Serializable, Comparable<SdkErrorCode>, Constable

public enum SdkErrorCode extends Enum<SdkErrorCode>
High-level, protocol-agnostic error categories recognised by the SDK.

These error codes provide a consistent way to classify failures across all SDK operations, allowing consumers to implement appropriate error handling and retry strategies.

Error Handling Strategy by Code


 try {
     result = sdk.encrypt("my-key", data);
 } catch (AnkaSecureSdkException e) {
     switch (e.getErrorCode()) {
         case TIMEOUT:
             // Retry with exponential backoff
             retryWithBackoff(operation);
             break;
         case IO:
             // Check network connectivity, verify TLS certificates
             validateNetworkAndRetry();
             break;
         case STATEFUL_KEY_BUSY:
             // Transient: retry after e.getRetryAfterSeconds()
             retryAfter(e.getRetryAfterSeconds(), operation);
             break;
         case STATEFUL_KEY_EXHAUSTED:
             // Permanent for this key: rotate it, do not retry
             scheduleRotation(kid);
             break;
         case HTTP:
             // Inspect HTTP status code for specific error
             handleHttpError(e.getHttpStatus(), e.getResponseBody());
             break;
         case UNKNOWN:
             // Log full context and alert operations team
             logger.error("Unknown error: {}", e.getMessage(), e);
             alertOps(e);
             break;
     }
 }
 
  • Nested Class Summary

    Nested classes/interfaces inherited from class java.lang.Enum

    Enum.EnumDesc<E extends Enum<E>>
  • Enum Constant Summary

    Enum Constants
    Enum Constant
    Description
    Remote endpoint replied with an HTTP 4xx (client error) or 5xx (server error).
    Generic I/O problem during HTTP communication.
    A JWE or JWS supplied by the caller is not a well-formed JOSE serialization the SDK can send, and was refused on the client before any request was made.
    Degenerate same-kid rotation rejected by the SDK's ReencryptInvariantGuard (defense-in-depth on top of the server-side REENCRYPT-as-first-class invariant).
    Another request is reserving the next one-time signature of the same stateful hash-based key (XMSS, XMSS^MT, LMS/HSS).
    A stateful hash-based signature key (XMSS, XMSS^MT, LMS/HSS) cannot produce another signature.
    Network stack detected a connect, read, or write timeout.
    Failure that cannot be classified into TIMEOUT, IO, or HTTP.
  • Method Summary

    Modifier and Type
    Method
    Description
    Returns the enum constant of this class with the specified name.
    static SdkErrorCode[]
    Returns an array containing the constants of this enum class, in the order they are declared.

    Methods inherited from class java.lang.Enum

    compareTo, describeConstable, equals, getDeclaringClass, hashCode, name, ordinal, toString, valueOf

    Methods inherited from class java.lang.Object

    getClass, notify, notifyAll, wait, wait, wait
  • Enum Constant Details

    • TIMEOUT

      public static final SdkErrorCode TIMEOUT
      Network stack detected a connect, read, or write timeout.

      Common causes:

      • API server is overloaded or unresponsive
      • Network latency exceeds configured timeout
      • Large file operations exceeding default timeout
      • Firewall or proxy blocking connection

      Recovery: Implement exponential backoff retry. Increase timeout in cli.properties (openapi.readTimeoutMs) if consistently timing out on legitimate operations.

      Example:

      
       // Retry with backoff
       int retries = 3;
       int delayMs = 1000;
       for (int i = 0; i < retries; i++) {
           try {
               return sdk.encrypt("key", data);
           } catch (AnkaSecureSdkException e) {
               if (e.getErrorCode() == SdkErrorCode.TIMEOUT && i < retries - 1) {
                   Thread.sleep(delayMs);
                   delayMs *= 2; // exponential backoff
               } else {
                   throw e;
               }
           }
       }
       
    • IO

      public static final SdkErrorCode IO
      Generic I/O problem during HTTP communication.

      Common causes:

      • Host unreachable (network down, incorrect API URL)
      • TLS/SSL handshake failure (invalid certificate, protocol mismatch)
      • DNS resolution failure
      • Connection reset by peer
      • Proxy configuration error

      Not a local file. A file the caller named that cannot be read or written is never IO: it is reported as LocalFileException, whose recovery is local.

      Recovery: Verify network connectivity, check openapi.host in cli.properties, validate TLS certificates, check proxy settings.

      Diagnostics: Check Throwable.getCause() for underlying IOException or SSLException details.

      Example:

      
       try {
           sdk = factory.authenticateApplication(clientId, clientSecret);
       } catch (AnkaSecureSdkException e) {
           if (e.getErrorCode() == SdkErrorCode.IO) {
               Throwable cause = e.getCause();
               if (cause instanceof SSLException) {
                   logger.error("TLS error: {}. Check server certificate.", cause.getMessage());
               } else if (cause instanceof UnknownHostException) {
                   logger.error("Cannot resolve host. Check openapi.host: {}", apiHost);
               }
           }
       }
       
    • HTTP

      public static final SdkErrorCode HTTP
      Remote endpoint replied with an HTTP 4xx (client error) or 5xx (server error).

      A refusal the SDK recognises by its RFC 9457 problem type carries its own code instead: STATEFUL_KEY_EXHAUSTED and STATEFUL_KEY_BUSY.

      Common HTTP status codes:

      • 400 Bad Request: Invalid input, malformed request, or key does not support operation
      • 401 Unauthorized: Invalid or expired authentication token
      • 403 Forbidden: Insufficient permissions or quota exceeded
      • 404 Not Found: Requested resource (e.g., key) does not exist
      • 409 Conflict: Resource already exists (e.g., duplicate key ID), or the key is in a state that refuses the operation
      • 429 Too Many Requests: Rate limit exceeded
      • 500 Internal Server Error: Server-side failure
      • 503 Service Unavailable: Server temporarily down or overloaded

      Recovery: Inspect AnkaSecureSdkException.getHttpStatus() and AnkaSecureSdkException.getResponseBody() for specific error details. Implement retry for 5xx errors, re-authenticate for 401, adjust request for 4xx errors.

      Example:

      
       try {
           sdk.encrypt(kid, data);
       } catch (AnkaSecureSdkException e) {
           if (e.getErrorCode() == SdkErrorCode.HTTP) {
               int status = e.getHttpStatus();
               String body = e.getResponseBody();
      
               if (status == 401) {
                   logger.warn("Token expired, re-authenticating...");
                   sdk = factory.authenticateApplication(clientId, clientSecret);
                   return sdk.encrypt(kid, data); // retry
               } else if (status == 404) {
                   logger.info("Key not found: {}", kid);
                   return sdk.getKeyMetadata(kid);
               } else if (status >= 500) {
                   logger.error("Server error {}: {}", status, body);
                   // implement retry with backoff
               } else {
                   logger.error("Client error {}: {}", status, body);
               }
           }
       }
       
    • UNKNOWN

      public static final SdkErrorCode UNKNOWN
      Failure that cannot be classified into TIMEOUT, IO, or HTTP.

      Common causes:

      • Unexpected exception in SDK internal logic
      • Malformed response from server (JSON parsing failure)
      • SDK bug or unsupported edge case
      • Runtime environment issue (missing classes, permissions)

      Check the exception's type first. A failure the caller can act on locally is reported as UNKNOWN and identified by its type, not by this code: LocalFileException (an input that cannot be read, a result that cannot be written), DestinationExistsException, StaleSidecarException. A failure of the HTTP exchange is never UNKNOWN; it is IO or TIMEOUT.

      Recovery: Otherwise this is typically non-recoverable. Log full exception details including AnkaSecureSdkException.getContext(), Throwable.getCause(), and AnkaSecureSdkException.getResponseBody() for diagnostics. Report to AnkaTech support if consistently occurring.

      Example:

      
       try {
           result = sdk.decrypt(jwe);
       } catch (AnkaSecureSdkException e) {
           if (e.getErrorCode() == SdkErrorCode.UNKNOWN) {
               logger.error("Unknown SDK error. Please report to support.");
               logger.error("Message: {}", e.getMessage());
               logger.error("Context: {}", e.getContext());
               logger.error("Response body: {}", e.getResponseBody());
               logger.error("Cause: ", e.getCause());
      
               // Alert operations team
               alertOps("Unknown SDK error", e);
           }
       }
       
    • REENCRYPT_DEGENERATE_SAME_KID

      public static final SdkErrorCode REENCRYPT_DEGENERATE_SAME_KID
      Degenerate same-kid rotation rejected by the SDK's ReencryptInvariantGuard (defense-in-depth on top of the server-side REENCRYPT-as-first-class invariant).

      Raised when a REENCRYPT or RESIGN response from the server carries newKeyRequested.equals(oldKeyRequested) — that is, the rotation collapsed to a no-op kid mapping. Cross-kid rotation is the purpose of these operations; same-kid is degenerate and must never succeed silently.

      The exception message distinguishes REENCRYPT vs RESIGN via the literal in the message body; a single error code is shared between both rotation primitives.

      Recovery: none — surface to the operator and fix the rotation request to use distinct kids.

    • MALFORMED_JOSE_INPUT

      public static final SdkErrorCode MALFORMED_JOSE_INPUT
      A JWE or JWS supplied by the caller is not a well-formed JOSE serialization the SDK can send, and was refused on the client before any request was made.

      Every operation that takes a JWE or JWS string or file (decrypt, re-encrypt, decrypt-then-verify, verify, re-sign) reads it as the Compact Serialization (RFC 7516 §7.1 / RFC 7515 §7.1) or the General JSON Serialization (§7.2), chosen by the content. The input is refused with this code when:

      • it is neither a JSON object nor the right number of dot-separated segments;
      • a Compact segment or a General JSON member is not base64url (RFC 7515 §2: the URL-safe alphabet, no padding), or the protected header does not decode to a JSON object;
      • a JSON input does not parse, repeats a member name, lacks the recipients / signatures array or a member the wire requires (a JWS entry's signature; a JWE recipient's encrypted_key is required only to be base64url when present — RFC 7516 §7.2.1 makes it absent for an empty key, as with alg=dir), or uses a form the wire does not carry: the flattened serialization (§7.2.2), a JWE aad or unprotected member;
      • a JWS declares an unencoded payload ("b64": false, RFC 7797) or has an empty signature (an unsecured JWS, RFC 7518 §3.6).

      The message names the serialization and the defect and never echoes the input; the exception carries no cause. AnkaSecureSdkException.getHttpStatus() is 0.

      Recovery: not retryable. Supply the artifact exactly as the SDK or the server produced it; convert a flattened serialization to the General one.

      A malformed JOSE artifact in a server response is not this code: it is a server contract violation, reported as UNKNOWN.

    • STATEFUL_KEY_EXHAUSTED

      public static final SdkErrorCode STATEFUL_KEY_EXHAUSTED
      A stateful hash-based signature key (XMSS, XMSS^MT, LMS/HSS) cannot produce another signature. Permanent for that key.

      Raised for an HTTP 409 whose problem type is https://docs.ankatech.co/errors/stateful-key-exhausted. Two causes share it: every one-time signature the key holds has been used, or the key existed in a database that was later restored, so the server can no longer prove an index has not been spent before. The refusal is decided before any output exists; a streaming operation writes no result.

      Every operation that produces a signature can raise it: sign, signFileCompact, signFileStream, signThenEncrypt*, resign* (the new key is the one that signs) and convertPkcs7ToJose*.

      Recovery: not retryable — the same request with the same key is refused again. Rotate the key (or sign with another one). The signatures the key already produced still verify.

      
        catch (AnkaSecureSdkException e) {
           if (e.getErrorCode() == SdkErrorCode.STATEFUL_KEY_EXHAUSTED) {
               logger.warn("Key {} has no one-time signature left; rotate it", kid);
           }
       }
       }
    • STATEFUL_KEY_BUSY

      public static final SdkErrorCode STATEFUL_KEY_BUSY
      Another request is reserving the next one-time signature of the same stateful hash-based key (XMSS, XMSS^MT, LMS/HSS). Transient.

      Raised for an HTTP 409 whose problem type is https://docs.ankatech.co/errors/stateful-key-busy. The server sends a Retry-After header (whole seconds, at least 1), exposed as AnkaSecureSdkException.getRetryAfterSeconds(). The key use the server already admitted is kept, and the refusal is decided before any output exists. Raised by the same operations as STATEFUL_KEY_EXHAUSTED.

      Recovery: retry the same request after AnkaSecureSdkException.getRetryAfterSeconds() seconds. A null value means the header was absent or not in integer-seconds form; wait at least one second.

      
        catch (AnkaSecureSdkException e) {
           if (e.getErrorCode() == SdkErrorCode.STATEFUL_KEY_BUSY) {
               Long seconds = e.getRetryAfterSeconds();
               Thread.sleep(1000L * (seconds == null ? 1L : seconds));
               return sdk.sign(kid, data);
           }
       }
       }
  • Method Details

    • values

      public static SdkErrorCode[] values()
      Returns an array containing the constants of this enum class, in the order they are declared.
      Returns:
      an array containing the constants of this enum class, in the order they are declared
    • valueOf

      public static SdkErrorCode valueOf(String name)
      Returns the enum constant of this class with the specified name. The string must match exactly an identifier used to declare an enum constant in this class. (Extraneous whitespace characters are not permitted.)
      Parameters:
      name - the name of the enum constant to be returned.
      Returns:
      the enum constant with the specified name
      Throws:
      IllegalArgumentException - if this enum class has no constant with the specified name
      NullPointerException - if the argument is null