CipherStash Docs

Error handling

Handle errors with the Result pattern used across the SDK

Error handling

All async methods in the SDK return a Result object — a discriminated union with either a data key (success) or a failure key (error). You never get both. The SDK never throws exceptions.

The Result pattern

result-check.ts
const result = await client.encrypt("hello@example.com", {
  column: users.email,
  table: users,
})

if (result.failure) {
  // result.failure.type: string (e.g. "EncryptionError")
  // result.failure.message: string (human-readable description)
  console.error(result.failure.type, result.failure.message)
} else {
  // result.data: Encrypted payload
  console.log(result.data)
}

This pattern applies to every async method: Encryption(), encrypt, decrypt, encryptModel, decryptModel, bulkEncrypt, bulkDecrypt, encryptQuery, and LockContext.identify().

Error types

TypeWhen it occursCommon causes
ClientInitErrorEncryption() initializationMissing or invalid credentials, unreachable ZeroKMS endpoint, no schemas provided
EncryptionErrorencrypt, encryptModel, bulkEncrypt, encryptQueryInvalid plaintext for the column's data type, NaN/Infinity for numeric columns, non-string value for freeTextSearch column
DecryptionErrordecrypt, decryptModel, bulkDecryptCorrupted ciphertext, wrong keyset, lock context mismatch (data was encrypted with a different identity)
LockContextErrorLockContext operationsInvalid JWT, missing required claims, CTS endpoint unreachable
CtsTokenErrorCTS token exchangeJWT rejected by CipherStash Token Service, expired token, CTS endpoint misconfigured

Handling initialization errors

Client initialization is the most common place to encounter errors. Unlike other SDK operations, Encryption() throws on failure rather than returning a Result.

init-error.ts
import { Encryption } from "@cipherstash/stack"
import { users } from "./schema"

try {
  const client = await Encryption({ schemas: [users] })
} catch (error) {
  // Check your CS_* environment variables
  console.error("Failed to initialize:", error.message)
  process.exit(1)
}

Common initialization issues

SymptomCauseFix
"Missing workspace CRN"CS_WORKSPACE_CRN not setSet the env var or pass workspaceCrn in config
"Missing client ID"CS_CLIENT_ID not setSet the env var or pass clientId in config
"Missing client key"CS_CLIENT_KEY not setSet the env var or pass clientKey in config
"Missing access key"CS_CLIENT_ACCESS_KEY not setSet the env var or pass accessKey in config
Connection timeoutZeroKMS endpoint unreachableCheck network connectivity and firewall rules

Handling encrypt/decrypt errors

encrypt-error.ts
const encrypted = await client.encrypt(value, {
  column: users.email,
  table: users,
})

if (encrypted.failure) {
  switch (encrypted.failure.type) {
    case "EncryptionError":
      // Log the error and decide whether to retry or fail
      console.error("Encryption failed:", encrypted.failure.message)
      break
  }
}

Common encrypt/decrypt issues

SymptomCauseFix
"NaN is not a valid value"Passed NaN to a numeric columnValidate inputs before encrypting
"Infinity is not a valid value"Passed Infinity to a numeric columnValidate inputs before encrypting
"Expected string value"Non-string value for a column with .freeTextSearch()Convert to string or remove the freeTextSearch index
Decryption fails with lock context errorData was encrypted with a different identityEnsure the same user's JWT is used for both encrypt and decrypt
Decryption returns corrupted data errorWrong keyset or tampered ciphertextVerify the keyset matches the one used during encryption

Handling bulk operation errors

Bulk operations (bulkDecrypt, bulkDecryptModels) support per-item error handling. The overall operation succeeds but individual items may fail.

bulk-error.ts
const result = await client.bulkDecrypt(encryptedPayloads)

if (result.failure) {
  // Entire operation failed (e.g., ZeroKMS unreachable)
  console.error("Bulk decrypt failed:", result.failure.message)
} else {
  // Per-item results
  for (const item of result.data) {
    if ("data" in item) {
      console.log(`${item.id}: decrypted successfully`)
    } else {
      // Individual item failed
      console.error(`${item.id}: ${item.error}`)
    }
  }
}

Handling identity errors

When using identity-aware encryption, errors can occur during JWT identification or when using lock contexts.

identity-error.ts
import { LockContext } from "@cipherstash/stack/identity"

const lc = new LockContext()
const identifyResult = await lc.identify(userJwt)

if (identifyResult.failure) {
  switch (identifyResult.failure.type) {
    case "LockContextError":
      // JWT is invalid or missing required claims
      console.error("Identity error:", identifyResult.failure.message)
      break
    case "CtsTokenError":
      // Token exchange with CTS failed
      console.error("CTS error:", identifyResult.failure.message)
      break
  }
}

Common identity issues

SymptomCauseFix
LockContextError with missing claimJWT doesn't contain the configured identity claimCheck your JWT contains the sub claim (or your custom claims)
CtsTokenError with connection errorCTS endpoint unreachableVerify CS_CTS_ENDPOINT is set correctly
CtsTokenError with rejectionJWT is expired or not trusted by CTSEnsure JWT is fresh and from a configured identity provider

Wrapping errors in application code

A common pattern is to create a helper that converts Result into thrown errors for frameworks that expect exceptions (e.g., Express, Next.js API routes):

helpers.ts
function unwrap<T>(result: { data?: T; failure?: { type: string; message: string } }): T {
  if (result.failure) {
    throw new Error(`[${result.failure.type}] ${result.failure.message}`)
  }
  return result.data as T
}

// Usage
const encrypted = unwrap(await client.encrypt("hello", { column: users.email, table: users }))

Good to know: The Result pattern is intentionally designed to avoid thrown exceptions. Use the unwrap helper only at your application's boundaries where exceptions are expected.

Logging

Control SDK log verbosity with the STASH_STACK_LOG environment variable:

STASH_STACK_LOG=error  # debug | info | error (default: error)
ValueWhat is logged
errorErrors only (default)
infoInfo and errors
debugDebug, info, and errors

The SDK never logs plaintext data or key material at any log level.

On this page