Credential Exchange Protocols - Troubleshooting
Common issues and solutions when working with credential exchange protocols.
Common Issues
Issue 1: Protocol Not Registered
Error:
1
ExchangeException.ProtocolNotRegistered: Protocol 'didcomm' not registered. Available: []
Error code: PROTOCOL_NOT_REGISTERED
Symptoms:
- Calling
offerCredential()throwsExchangeException.ProtocolNotRegistered registry.getAllProtocolNames()returns empty list
Solutions:
- Register the protocol:
1 2 3
val registry = CredentialExchangeProtocolRegistry() val didCommService = DidCommFactory.createInMemoryService(kms, resolveDid) registry.register(DidCommExchangeProtocol(didCommService))
- Check if protocol is registered:
1 2 3 4
if (!registry.isRegistered("didcomm")) { println("Protocol not registered. Registering...") registry.register(DidCommExchangeProtocol(didCommService)) }
- Verify protocol name:
1 2 3
val available = registry.getAllProtocolNames() println("Available protocols: $available") // Make sure you're using the correct protocol name
Prevention:
- Always register protocols before use
- Check
registry.isRegistered()before calling methods - Use consistent protocol names
Issue 2: Missing Required Options
Error:
1
ExchangeException.MissingRequiredOption: Missing required option 'fromKeyId' for protocol 'didcomm'
Error code: MISSING_REQUIRED_OPTION
Symptoms:
- DIDComm operations fail with missing option error
- Options map is empty or incomplete
Solutions:
- Add required options for DIDComm:
1 2 3 4
options = mapOf( "fromKeyId" to "did:key:issuer#key-1", // Required "toKeyId" to "did:key:holder#key-1" // Required )
- Add required options for OIDC4VCI:
1 2 3
options = mapOf( "credentialIssuer" to "https://issuer.example.com" // Required )
- Check protocol-specific requirements:
- See API Reference for required options
- See Protocol-Specific Options
Prevention:
- Always check protocol documentation for required options
- Validate options before calling methods
- Use helper functions to build options
Issue 3: Operation Not Supported
Error:
1
ExchangeException.OperationNotSupported: Protocol 'oidc4vci' does not support operation 'REQUEST_PROOF'. Supported: [OFFER_CREDENTIAL, REQUEST_CREDENTIAL, ISSUE_CREDENTIAL]
Error code: OPERATION_NOT_SUPPORTED
Symptoms:
- Calling
requestProof()throwsExchangeException.OperationNotSupported - Protocol doesn’t support the requested operation
Solutions:
- Check supported operations:
1 2 3 4 5
val protocol = registry.get("oidc4vci") if (protocol != null) { println("Supported operations: ${protocol.supportedOperations}") // Output: [OFFER_CREDENTIAL, REQUEST_CREDENTIAL, ISSUE_CREDENTIAL] }
- Use a different protocol:
1 2
// OIDC4VCI doesn't support proof requests, use DIDComm val proofRequest = registry.requestProof("didcomm", request)
- Use a different operation:
1 2 3 4
// If you need proof functionality, use DIDComm or CHAPI if (registry.isRegistered("didcomm")) { val proofRequest = registry.requestProof("didcomm", request) }
Prevention:
- Check
protocol.supportedOperationsbefore calling methods - Use protocol comparison table to choose the right protocol
- Handle
ExchangeException.OperationNotSupportedgracefully
Issue 4: Invalid DID Format
Error:
1
IllegalArgumentException: Invalid DID format: invalid-did
Symptoms:
- DID validation fails
- Operations fail with format error
Solutions:
- Validate DID format:
1 2 3 4 5 6 7 8 9
fun isValidDid(did: String): Boolean { return did.matches(Regex("^did:[a-z0-9]+:.+$")) } val issuerDid = "did:key:issuer" if (!isValidDid(issuerDid)) { println("Invalid DID format") return }
- Check DID format requirements:
- Format:
did:<method>:<identifier> - Method must be lowercase alphanumeric
- Identifier must not be empty
- Format:
Prevention:
- Always validate DID format before use
- Use DID creation methods instead of manual construction
- Check for typos in DID strings
Issue 5: Key Not Found in KMS
Error:
1
IllegalStateException: Key not found: did:key:issuer#key-1
Symptoms:
- DIDComm operations fail with key not found
- KMS doesn’t contain the referenced key
Solutions:
- Ensure key exists in KMS:
1 2 3 4 5
val keyId = "did:key:issuer#key-1" if (!kms.keyExists(keyId)) { // Create key or use existing key kms.createKey(keyId, algorithm = "Ed25519") }
- Use correct key ID:
1 2 3 4
// Get key ID from DID document val document = resolveDid("did:key:issuer") val keyId = document?.verificationMethod?.firstOrNull()?.id ?: throw IllegalStateException("No verification method found")
Prevention:
- Always create keys before use
- Use key IDs from DID documents
- Verify key exists in KMS before operations
Issue 6: DID Resolution Failure
Error:
1
IllegalStateException: Failed to resolve DID: did:key:issuer
Symptoms:
- DID resolution returns null
- Operations fail with resolution error
Solutions:
- Implement proper DID resolution:
1 2 3 4
val resolveDid: suspend (String) -> DidDocument? = { did -> // Implement real DID resolution yourDidResolver.resolve(did) }
- Check DID resolver configuration:
1 2 3 4 5 6
// Ensure DID resolver is properly configured val document = resolveDid("did:key:issuer") if (document == null) { println("DID not resolvable") return }
Prevention:
- Use proper DID resolver implementation
- Test DID resolution before operations
- Handle resolution failures gracefully
Issue 7: HTTP Request Failure (OIDC4VCI)
Error:
1
Exception: HTTP request failed: 404 Not Found
Symptoms:
- OIDC4VCI operations fail with HTTP errors
- Credential issuer endpoint not reachable
Solutions:
- Check credential issuer URL:
1 2
val credentialIssuer = "https://issuer.example.com" // Verify URL is correct and reachable
- Handle HTTP errors:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
try { val offer = registry.offerCredential("oidc4vci", request) } catch (e: Exception) { when { e.message?.contains("404") == true -> { println("Credential issuer not found. Check URL.") } e.message?.contains("401") == true -> { println("Authentication failed. Check credentials.") } e.message?.contains("timeout") == true -> { println("Request timed out. Retry later.") } } }
Prevention:
- Verify credential issuer URL is correct
- Test connectivity before operations
- Handle HTTP errors gracefully
Debugging Tips
Tip 1: Enable Logging
1
2
3
// Enable debug logging
val registry = CredentialExchangeProtocolRegistry()
// Add logging interceptor or enable debug mode
Tip 2: Check Protocol State
1
2
3
4
5
6
// Check what protocols are registered
val protocols = registry.getAll()
protocols.forEach { (name, protocol) ->
println("Protocol: $name")
println(" Supported operations: ${protocol.supportedOperations}")
}
Tip 3: Validate Inputs
1
2
3
4
5
6
// Validate all inputs before operations
fun validateOfferRequest(request: CredentialOfferRequest): Boolean {
return isValidDid(request.issuerDid) &&
isValidDid(request.holderDid) &&
request.credentialPreview.attributes.isNotEmpty()
}
Tip 4: Use Try-Catch
1
2
3
4
5
6
7
8
9
10
11
// Always use try-catch for error handling
try {
val offer = registry.offerCredential("didcomm", request)
} catch (e: IllegalArgumentException) {
println("Invalid argument: ${e.message}")
} catch (e: UnsupportedOperationException) {
println("Unsupported operation: ${e.message}")
} catch (e: Exception) {
println("Unexpected error: ${e.message}")
e.printStackTrace()
}
Getting Help
If you encounter issues not covered here:
- Check Documentation:
- Check Examples:
- File an Issue:
- Include error message
- Include code snippet
- Include protocol and options used
Related Documentation
- Quick Start - Get started quickly (5 minutes)
- API Reference - Complete API documentation
- Error Handling - Error handling guide
- Workflows - Step-by-step workflows
- Examples - Code examples
- Best Practices - Best practices and patterns
- Glossary - Terms and concepts