TrustWeave provides structured error handling with rich context for better debugging and error recovery.
Overview
Important: The TrustWeave facade methods throw exceptions on failure, not Result<T>. All TrustWeave methods are suspend functions that throw exceptions when operations fail.
Exception-Based Error Handling:
All TrustWeave facade methods throw domain-specific exceptions
Use try-catch blocks for error handling
Domain-specific exceptions: DidException, CredentialException, WalletException, etc.
All exceptions extend TrustWeaveException with error codes and context
Result-Based Error Handling:
Some lower-level service APIs may return Result<T> for functional composition
These are typically internal APIs or service interfaces
The TrustWeave facade converts these to exceptions for simpler usage
TrustWeave Facade Error Handling
The TrustWeave facade methods throw exceptions on failure. Always use try-catch blocks:
importcom.trustweave.did.exception.DidExceptionimportcom.trustweave.did.exception.DidException.DidMethodNotRegisteredimportcom.trustweave.did.exception.DidException.DidNotFoundimportcom.trustweave.did.exception.DidException.InvalidDidFormat// Handle DID errors with short importstry{valdid=trustWeave.createDid{method("key")}}catch(error:DidException){when(error){isDidMethodNotRegistered->{println("Method not registered: ${error.method}")println("Available: ${error.availableMethods}")}isDidNotFound->{println("DID not found: ${error.did}")}isInvalidDidFormat->{println("Invalid format: ${error.reason}")}}}
Credential-Related Errors (in trustweave-credentials module)
importcom.trustweave.core.exception.TrustWeaveException// Invalid JSONTrustWeaveException.InvalidJson(jsonString="{ invalid }",parseError="Expected ',' or '}'",position="line 1, column 10")// JSON encoding failedTrustWeaveException.JsonEncodeFailed(element="{ large object }",reason="Circular reference detected")// Digest computation failedTrustWeaveException.DigestFailed(algorithm="SHA-256",reason="Algorithm not available")// Encoding failedTrustWeaveException.EncodeFailed(operation="base58-encoding",reason="Invalid byte array")
Validation Errors
1
2
3
4
5
6
7
8
importcom.trustweave.core.exception.TrustWeaveException// Validation failedTrustWeaveException.ValidationFailed(field="issuer",reason="Invalid DID format",value="invalid-did")
importcom.trustweave.core.exception.TrustWeaveException// Invalid operationTrustWeaveException.InvalidOperation(code="INVALID_OPERATION",message="Operation not allowed in current state",context=mapOf("operation"to"createDid","state"to"stopped"),cause=null)// Invalid stateTrustWeaveException.InvalidState(code="INVALID_STATE",message="TrustWeave not initialized",context=emptyMap(),cause=null)// Resource not foundTrustWeaveException.NotFound(resource="did:key:z6Mk...",message="Resource not found: did:key:z6Mk...",context=emptyMap(),cause=null)// Unknown error (catch-all)TrustWeaveException.Unknown(code="UNKNOWN_ERROR",message="Unexpected error occurred",context=emptyMap(),cause=originalException)
Error Code Quick Reference
Quick lookup table for common error codes and their solutions:
Code
Error Type
Common Causes
Solutions
DID_NOT_FOUND
DidNotFound
DID not resolvable, method not registered, network issue
Check DID format, ensure method registered, verify network connectivity
DID_METHOD_NOT_REGISTERED
DidMethodNotRegistered
Method not in registry
Register method via registerDidMethod() or use available method from getAvailableDidMethods()
INVALID_DID_FORMAT
InvalidDidFormat
DID doesn’t match did:<method>:<identifier> format
// Handle errors gracefullyvaldid=TrustWeave.dids.create()// Note: dids.create() returns DidDocument directly, not Result// For error handling, wrap in try-catchresult.fold(onSuccess={did->// Process DIDprocessDid(did)},onFailure={error->// Handle error appropriatelylogger.error("Failed to create DID",error)// Show user-friendly message or retry})
Why:getOrThrow() throws exceptions that can crash your application. Use fold() for production code.
result.fold(onFailure={error->logger.error("Error: ${error.message}")logger.debug("Error code: ${error.code}")logger.debug("Context: ${error.context}")// Use context for better error handlingwhen(error){isDidException.DidMethodNotRegistered->{logger.info("Available methods: ${error.availableMethods}")// Suggest alternatives to user}isBlockchainException.ChainNotRegistered->{logger.info("Available chains: ${error.availableChains}")// Suggest fallback chains}// ... handle other specific errors}})
Why: Error context contains valuable debugging information and alternative options.
Pitfall 3: Ignoring Warnings in Verification Results
❌ Bad:
1
2
3
4
5
valverification=TrustWeave.verifyCredential(credential).getOrThrow()if(verification.valid){// Use credential without checking warningsprocessCredential(credential)}
valverification=TrustWeave.verifyCredential(credential).getOrThrow()if(verification.valid){// Check warnings before usingif(verification.warnings.isNotEmpty()){logger.warn("Credential has warnings: ${verification.warnings}")// Decide if warnings are acceptable for your use case}// Check specific validation flagsif(!verification.proofValid){logger.error("Proof validation failed")return}if(!verification.notRevoked){logger.warn("Credential may be revoked")// Handle revocation appropriately}processCredential(credential)}
Why: Warnings indicate potential issues that may affect credential validity in the future.
Pitfall 4: Not Validating Inputs Before Operations
❌ Bad:
1
2
// No validation, may fail with cryptic errorvalresolution=TrustWeave.dids.resolve(userInputDid)
✅ Good:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Validate before operationvalvalidation=DidValidator.validateFormat(userInputDid)if(!validation.isValid()){valerror=validationasValidationResult.InvalidreturnResult.failure(DidException.InvalidDidFormat(did=userInputDid,reason=error.message))}// Now safe to proceedvalresolution=TrustWeave.dids.resolve(userInputDid)
Why: Early validation provides better error messages and prevents unnecessary operations.
Pitfall 5: Not Handling Specific Error Types
❌ Bad:
1
2
3
4
5
6
result.fold(onFailure={error->// Generic handling loses specific error informationprintln("Something went wrong: ${error.message}")})
result.fold(onFailure={error->when(error){isDidException.DidMethodNotRegistered->{// Specific handling for method not registeredlogger.warn("Method not registered: ${error.method}")logger.info("Available methods: ${error.availableMethods}")// Register method or suggest alternatives}isDidException.InvalidDidFormat->{// Specific handling for invalid formatlogger.error("Invalid DID format: ${error.reason}")// Show format requirements to user}isCredentialException.CredentialInvalid->{// Specific handling for invalid credentiallogger.error("Credential invalid: ${error.reason}")logger.debug("Field: ${error.field}")// Fix credential or reject}else->{// Generic handling for unknown errorslogger.error("Unexpected error: ${error.message}",error)}}})
Why: Specific error types provide actionable information for recovery.
importcom.trustweave.TrustWeaveimportcom.trustweave.core.*valTrustWeave=TrustWeave.create()// Handle errors with foldvaldid=TrustWeave.dids.create()// Note: dids.create() returns DidDocument directly, not Result// For error handling, wrap in try-catchresult.fold(onSuccess={did->println("Created DID: ${did.id}")},onFailure={error->when(error){isDidException.DidMethodNotRegistered->{println("Method not registered: ${error.method}")println("Available methods: ${error.availableMethods}")}else->{println("Error: ${error.message}")error.context.forEach{(key,value)->println(" $key: $value")}}}})
Using getOrThrow for Simple Cases
1
2
3
4
5
// For simple cases where you want to throw on errorvaldid=TrustWeave.dids.create()// For better error messages, use getOrThrowErrorvaldid=TrustWeave.dids.create()// Throws TrustWeaveException on failure
Error Context
All errors include context information that can help with debugging:
TrustWeave automatically converts exceptions to TrustWeaveException:
1
2
3
4
5
6
7
8
9
10
importcom.trustweave.core.toTrustWeaveExceptiontry{// Some operation that might throwvalresult=someOperation()}catch(e:Exception){valerror=e.toTrustWeaveException()println("Error code: ${error.code}")println("Context: ${error.context}")}
Result Utilities
TrustWeave provides extension functions for working with Result<T>:
mapError
Transform errors in a Result:
1
2
3
4
valdid=TrustWeave.dids.create()// Note: dids.create() returns DidDocument directly, not Result// For error handling, wrap in try-catch.mapError{it.toTrustWeaveException()}
TrustWeave validates inputs before operations to catch errors early:
DID Validation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
importcom.trustweave.core.util.DidValidator// Validate DID formatvalvalidation=DidValidator.validateFormat("did:key:z6Mk...")if(!validation.isValid()){valerror=validationasValidationResult.Invalidprintln("Validation failed: ${error.message}")println("Field: ${error.field}")println("Value: ${error.value}")}// Validate DID methodvalavailableMethods=listOf("key","web")valmethodValidation=DidValidator.validateMethod("did:key:z6Mk...",availableMethods)if(!methodValidation.isValid()){println("Method not supported")}
importcom.trustweave.core.ChainIdValidator// Validate chain ID formatvalvalidation=ChainIdValidator.validateFormat("algorand:testnet")if(!validation.isValid()){println("Invalid chain ID format")}// Validate chain is registeredvalavailableChains=listOf("algorand:testnet","polygon:testnet")valregisteredValidation=ChainIdValidator.validateRegistered("ethereum:mainnet",availableChains)if(!registeredValidation.isValid()){println("Chain not registered")}
Best Practices
1. Always Handle Errors
1
2
3
4
5
6
7
8
9
10
11
// ❌ Bad: Ignoring errorsvaldid=TrustWeave.dids.create()// ✅ Good: Handling errors explicitlyvaldid=TrustWeave.dids.create()// Note: dids.create() returns DidDocument directly, not Result// For error handling, wrap in try-catchresult.fold(onSuccess={did->/* handle success */},onFailure={error->/* handle error */})
2. Use Error Context
1
2
3
4
5
6
7
8
9
10
// ✅ Good: Use error context for debuggingvalresult=TrustWeave.anchor(data,serializer,chainId)result.fold(onSuccess={/* success */},onFailure={error->logger.error("Anchoring failed",error)logger.debug("Error context: ${error.context}")logger.debug("Error code: ${error.code}")})
// ✅ Good: Handle specific error typesvaldid=TrustWeave.dids.create(method="web")result.fold(onSuccess={/* success */},onFailure={error->when(error){isDidException.DidMethodNotRegistered->{// Suggest available methodsprintln("Method 'web' not available. Try: ${error.availableMethods}")}isDidException.InvalidDidFormat->{// Show format requirementsprintln("Invalid format: ${error.reason}")}else->{// Generic error handlingprintln("Error: ${error.message}")}}})
4. Validate Inputs Early
1
2
3
4
5
6
7
8
// ✅ Good: Validate before operationvaldid="did:key:z6Mk..."valvalidation=DidValidator.validateFormat(did)if(!validation.isValid()){returnResult.failure(DidException.InvalidDidFormat(did,validation.errorMessage()?:""))}valresolution=TrustWeave.dids.resolve(did)
5. Use Result Utilities
1
2
3
4
5
6
7
8
9
10
11
12
// ✅ Good: Use combine for batch operationsvaldids=listOf("did:key:1","did:key:2","did:key:3")valresults=dids.map{TrustWeave.dids.resolve(it)}valcombined=results.combine{resolutions->resolutions.mapNotNull{it.document?.id}}combined.fold(onSuccess={ids->println("Resolved: $ids")},onFailure={error->println("Error: ${error.message}")})
Plugin Lifecycle Errors
When managing plugin lifecycles, errors are handled automatically:
importkotlinx.coroutines.delayimportkotlin.random.Randomimportcom.trustweave.core.exception.TrustWeaveExceptionimportcom.trustweave.did.exception.DidExceptionimportcom.trustweave.credential.exception.CredentialExceptionsuspendfun<T>retryWithBackoff(maxRetries:Int=3,initialDelay:Long=1000,maxDelay:Long=10000,multiplier:Double=2.0,operation:suspend()->Result<T>):Result<T>{vardelay=initialDelay.toDouble()varlastError:TrustWeaveException?=nullrepeat(maxRetries){attempt->valresult=operation()result.fold(onSuccess={returnresult},onFailure={error->lastError=error// Don't retry on certain errorswhen(error){isDidException.InvalidDidFormat,isCredentialException.CredentialInvalid,isTrustWeaveException.ValidationFailed->{returnresult// Don't retry validation errors}else->{if(attempt<maxRetries-1){valjitter=Random.nextLong(0,(delay*0.1).toLong())valactualDelay=minOf((delay+jitter).toLong(),maxDelay)delay(actualDelay)delay*=multiplier}}}})}returnResult.failure(lastError?:TrustWeaveException.Unknown(code="RETRY_EXHAUSTED",message="Operation failed after $maxRetries retries",context=emptyMap(),cause=null))}// Usagevalresult=retryWithBackoff{TrustWeave.dids.resolve("did:web:example.com")}
suspendfuncreateDidWithAutoRegistration(method:String,options:DidCreationOptions?=null):Result<DidDocument>{valdid=TrustWeave.dids.create(method,options)returnresult.fold(onSuccess={did->Result.success(did)},onFailure={error->when(error){isDidException.DidMethodNotRegistered->{// Try to find and register the methodvalmethodClass=findDidMethodClass(method)if(methodClass!=null){TrustWeave.registerDidMethod(methodClass)// Retry after registrationTrustWeave.dids.create(method,options)}else{Result.failure(error)}}else->Result.failure(error)}})}// Helper to find method class (implementation depends on your SPI setup)funfindDidMethodClass(method:String):DidMethod?{// Use ServiceLoader or reflection to find available methodsreturnServiceLoader.load(DidMethod::class.java).firstOrNull{it.methodName()==method}}
Circuit Breaker Pattern
Prevent cascading failures with a circuit breaker:
suspendfunverifyCredentialWithFallback(credential:VerifiableCredential,strictMode:Boolean=false):CredentialVerificationResult{valresult=TrustWeave.verifyCredential(credential).getOrNull()if(result!=null&&result.valid){returnresult}// Fallback: Basic validation without network callsif(!strictMode){valbasicValidation=CredentialValidator.validateStructure(credential)if(basicValidation.isValid()){returnCredentialVerificationResult(valid=true,proofValid=false,// Unknown without networknotExpired=credential.expirationDate?.let{Instant.parse(it).isAfter(Instant.now())}?:true,notRevoked=null,// Unknown without networkwarnings=listOf("Full verification unavailable, using basic validation"),errors=emptyList())}}returnresult?:CredentialVerificationResult(valid=false,errors=listOf("Verification failed and no fallback available"))}
importkotlinx.coroutines.withTimeoutimportkotlinx.coroutines.TimeoutCancellationExceptionsuspendfun<T>withTimeoutOrError(timeoutMillis:Long,operation:suspend()->Result<T>):Result<T>{returntry{withTimeout(timeoutMillis){operation()}}catch(e:TimeoutCancellationException){Result.failure(TrustWeaveException.Unknown(code="OPERATION_TIMEOUT",message="Operation timed out after ${timeoutMillis}ms",context=mapOf("timeout"totimeoutMillis.toString()),cause=e))}}// Usagevalresult=withTimeoutOrError(5000){TrustWeave.dids.resolve("did:web:example.com")}
Exception vs Sealed Result Patterns
For a detailed explanation of when to use exceptions vs sealed results, see Error Handling Patterns: