Documentation Index Fetch the complete documentation index at: https://core.anylayer.org/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The ZKScore SDK provides detailed error information to help you build robust applications. This guide covers error types, handling strategies, and best practices.
Error Structure
All SDK errors follow a consistent structure:
{
code : 'ERROR_CODE' ,
message : 'Human-readable error message' ,
details : {
// Additional context
},
statusCode : 400 ,
timestamp : '2024-10-23T14:30:00Z'
}
Common Error Codes
Authentication Errors
Code : INVALID_API_KEY
Status : 401The provided API key is invalid or has been revoked. try {
await sdk . scores . getScore ( address );
} catch ( error ) {
if ( error . code === 'INVALID_API_KEY' ) {
console . log ( 'Please check your API key' );
// Redirect to API key management
}
}
Code : API_KEY_EXPIRED
Status : 401The API key has expired and needs to be renewed.
Code : UNAUTHORIZED
Status : 403The API key doesn’t have permission for this operation.
Resource Errors
Code : IDENTITY_NOT_FOUND
Status : 404The address doesn’t have a ZKScore identity. try {
const identity = await sdk . identity . getIdentity ( address );
} catch ( error ) {
if ( error . code === 'IDENTITY_NOT_FOUND' ) {
// Prompt user to create identity
const newIdentity = await sdk . identity . mint ({
address ,
username: 'alice' ,
});
}
}
Code : USERNAME_TAKEN
Status : 409The requested username is already in use.
Code : ACHIEVEMENT_NOT_FOUND
Status : 404The specified achievement doesn’t exist.
Code : ATTESTATION_NOT_FOUND
Status : 404The attestation ID doesn’t exist or has been revoked.
Rate Limiting
Code : RATE_LIMIT_EXCEEDED
Status : 429Too many requests. Includes retry-after information. try {
await sdk . scores . getScore ( address );
} catch ( error ) {
if ( error . code === 'RATE_LIMIT_EXCEEDED' ) {
const retryAfter = error . details . retryAfter ; // seconds
console . log ( `Rate limited. Retry after ${ retryAfter } s` );
// Wait and retry
await new Promise ( resolve => setTimeout ( resolve , retryAfter * 1000 ));
return sdk . scores . getScore ( address );
}
}
Validation Errors
Code : INVALID_ADDRESS
Status : 400The provided Ethereum address is invalid.
Code : INVALID_PARAMETERS
Status : 400Request parameters are invalid or missing. try {
await sdk . achievements . claim ({
achievementId: '' , // Invalid
});
} catch ( error ) {
if ( error . code === 'INVALID_PARAMETERS' ) {
console . log ( 'Validation errors:' , error . details . errors );
// {
// achievementId: 'Required field',
// proof: 'Invalid proof format'
// }
}
}
Code : INVALID_SCHEMA
Status : 400Attestation schema is invalid or doesn’t exist.
Network Errors
Code : NETWORK_ERROR
Status : 500Network connection failed.
Code : TIMEOUT
Status : 504Request timed out.
Code : SERVICE_UNAVAILABLE
Status : 503The service is temporarily unavailable.
Data Errors
Code : INSUFFICIENT_DATA
Status : 400Not enough data to complete the operation. try {
const proof = await sdk . zkProofs . generate ({
address ,
type: 'trading-volume' ,
minVolume: '100000' ,
});
} catch ( error ) {
if ( error . code === 'INSUFFICIENT_DATA' ) {
console . log ( 'User needs more trading history' );
}
}
Code : SCORE_NOT_READY
Status : 202Score is still being calculated.
Error Handling Patterns
Basic Try-Catch
async function getScoreSafely ( address : string ) {
try {
const score = await sdk . scores . getScore ( address );
return { success: true , data: score };
} catch ( error ) {
return {
success: false ,
error: {
code: error . code ,
message: error . message ,
},
};
}
}
Specific Error Handling
async function mintIdentityWithErrorHandling ( address : string , username : string ) {
try {
const identity = await sdk . identity . mint ({ address , username });
return { success: true , identity };
} catch ( error ) {
switch ( error . code ) {
case 'USERNAME_TAKEN' :
return {
success: false ,
error: 'This username is already taken. Please choose another.' ,
};
case 'INVALID_ADDRESS' :
return {
success: false ,
error: 'Invalid Ethereum address provided.' ,
};
case 'IDENTITY_ALREADY_EXISTS' :
return {
success: false ,
error: 'This address already has an identity.' ,
};
case 'RATE_LIMIT_EXCEEDED' :
return {
success: false ,
error: 'Too many requests. Please try again later.' ,
};
default :
return {
success: false ,
error: 'An unexpected error occurred. Please try again.' ,
};
}
}
}
Retry Logic
async function withRetry < T >(
fn : () => Promise < T >,
maxRetries : number = 3 ,
delayMs : number = 1000
) : Promise < T > {
let lastError : any ;
for ( let i = 0 ; i < maxRetries ; i ++ ) {
try {
return await fn ();
} catch ( error ) {
lastError = error ;
// Don't retry on certain errors
if ([ 'INVALID_API_KEY' , 'INVALID_ADDRESS' , 'USERNAME_TAKEN' ]. includes ( error . code )) {
throw error ;
}
// Exponential backoff
const delay = delayMs * Math . pow ( 2 , i );
console . log ( `Retry ${ i + 1 } / ${ maxRetries } after ${ delay } ms` );
await new Promise ( resolve => setTimeout ( resolve , delay ));
}
}
throw lastError ;
}
// Usage
const score = await withRetry (() => sdk . scores . getScore ( address ));
Rate Limit Handler
class RateLimitHandler {
private queue : Array <() => Promise < any >> = [];
private processing = false ;
private lastRequest = 0 ;
private minInterval = 1000 ; // 1 second between requests
async execute < T >( fn : () => Promise < T >) : Promise < T > {
return new Promise (( resolve , reject ) => {
this . queue . push ( async () => {
try {
const result = await fn ();
resolve ( result );
} catch ( error ) {
if ( error . code === 'RATE_LIMIT_EXCEEDED' ) {
// Re-queue the request
this . queue . unshift ( fn );
const retryAfter = error . details . retryAfter * 1000 ;
await new Promise ( r => setTimeout ( r , retryAfter ));
} else {
reject ( error );
}
}
});
if ( ! this . processing ) {
this . processQueue ();
}
});
}
private async processQueue () {
this . processing = true ;
while ( this . queue . length > 0 ) {
const now = Date . now ();
const timeSinceLastRequest = now - this . lastRequest ;
if ( timeSinceLastRequest < this . minInterval ) {
await new Promise ( resolve =>
setTimeout ( resolve , this . minInterval - timeSinceLastRequest )
);
}
const fn = this . queue . shift ();
if ( fn ) {
this . lastRequest = Date . now ();
await fn ();
}
}
this . processing = false ;
}
}
const rateLimiter = new RateLimitHandler ();
// Usage
const score = await rateLimiter . execute (() => sdk . scores . getScore ( address ));
Circuit Breaker
class CircuitBreaker {
private failureCount = 0 ;
private successCount = 0 ;
private lastFailureTime = 0 ;
private state : 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED' ;
constructor (
private threshold : number = 5 ,
private timeout : number = 60000
) {}
async execute < T >( fn : () => Promise < T >) : Promise < T > {
if ( this . state === 'OPEN' ) {
if ( Date . now () - this . lastFailureTime > this . timeout ) {
this . state = 'HALF_OPEN' ;
} else {
throw new Error ( 'Circuit breaker is OPEN' );
}
}
try {
const result = await fn ();
this . onSuccess ();
return result ;
} catch ( error ) {
this . onFailure ();
throw error ;
}
}
private onSuccess () {
this . failureCount = 0 ;
this . successCount ++ ;
if ( this . state === 'HALF_OPEN' && this . successCount >= 2 ) {
this . state = 'CLOSED' ;
this . successCount = 0 ;
}
}
private onFailure () {
this . failureCount ++ ;
this . lastFailureTime = Date . now ();
if ( this . failureCount >= this . threshold ) {
this . state = 'OPEN' ;
}
}
}
const breaker = new CircuitBreaker ();
// Usage
const score = await breaker . execute (() => sdk . scores . getScore ( address ));
Error Logging
class ErrorLogger {
async logError ( error : any , context : any = {}) {
const errorLog = {
code: error . code ,
message: error . message ,
statusCode: error . statusCode ,
timestamp: new Date (). toISOString (),
context ,
stack: error . stack ,
};
// Send to logging service
console . error ( 'ZKScore Error:' , errorLog );
// Could also send to external service
// await sendToSentry(errorLog);
}
}
const logger = new ErrorLogger ();
// Usage
try {
await sdk . scores . getScore ( address );
} catch ( error ) {
await logger . logError ( error , {
operation: 'getScore' ,
address ,
userId: currentUser . id ,
});
throw error ;
}
Graceful Degradation
async function getScoreWithFallback ( address : string ) {
try {
return await sdk . scores . getScore ( address );
} catch ( error ) {
if ( error . code === 'SCORE_NOT_READY' ) {
// Return estimated score
return {
overall: 500 ,
breakdown: {},
estimated: true ,
message: 'Score is being calculated' ,
};
}
if ( error . code === 'SERVICE_UNAVAILABLE' ) {
// Return cached score if available
const cached = await getCachedScore ( address );
if ( cached ) {
return {
... cached ,
cached: true ,
message: 'Showing cached score' ,
};
}
}
// Re-throw if no fallback available
throw error ;
}
}
Error Boundaries (React)
import { Component , ReactNode } from 'react' ;
interface Props {
children : ReactNode ;
fallback ?: ReactNode ;
}
interface State {
hasError : boolean ;
error ?: any ;
}
class ZKScoreErrorBoundary extends Component < Props , State > {
constructor ( props : Props ) {
super ( props );
this . state = { hasError: false };
}
static getDerivedStateFromError ( error : any ) {
return {
hasError: true ,
error ,
};
}
componentDidCatch ( error : any , errorInfo : any ) {
console . error ( 'ZKScore Error:' , error , errorInfo );
// Log to error tracking service
// logErrorToService(error, errorInfo);
}
render () {
if ( this . state . hasError ) {
return this . props . fallback || (
< div className = "error-container" >
< h2 > Something went wrong </ h2 >
< p > { this . state . error ?. message } </ p >
< button onClick = { () => this . setState ({ hasError: false }) } >
Try Again
</ button >
</ div >
);
}
return this . props . children ;
}
}
// Usage
< ZKScoreErrorBoundary >
< Dashboard />
</ ZKScoreErrorBoundary >
Validation Helper
function validateAddress ( address : string ) : void {
if ( ! address || typeof address !== 'string' ) {
throw {
code: 'INVALID_ADDRESS' ,
message: 'Address is required' ,
statusCode: 400 ,
};
}
if ( ! / ^ 0x [ a-fA-F0-9 ] {40} $ / . test ( address )) {
throw {
code: 'INVALID_ADDRESS' ,
message: 'Invalid Ethereum address format' ,
statusCode: 400 ,
};
}
}
// Usage
try {
validateAddress ( userInput );
const score = await sdk . scores . getScore ( userInput );
} catch ( error ) {
// Handle validation error
}
TypeScript Error Types
interface ZKScoreError {
code : string ;
message : string ;
details ?: Record < string , any >;
statusCode : number ;
timestamp : string ;
}
type ErrorCode =
| 'INVALID_API_KEY'
| 'API_KEY_EXPIRED'
| 'UNAUTHORIZED'
| 'IDENTITY_NOT_FOUND'
| 'USERNAME_TAKEN'
| 'RATE_LIMIT_EXCEEDED'
| 'INVALID_ADDRESS'
| 'INVALID_PARAMETERS'
| 'NETWORK_ERROR'
| 'TIMEOUT'
| 'SERVICE_UNAVAILABLE'
| 'INSUFFICIENT_DATA'
| 'SCORE_NOT_READY' ;
Best Practices
Always Handle Errors Wrap all SDK calls in try-catch blocks
Provide Feedback Show user-friendly error messages
Log Errors Track errors for debugging and monitoring
Implement Retries Retry transient failures with backoff
Validate Input Validate before making API calls
Graceful Degradation Provide fallbacks when possible
Next Steps
Rate Limiting Guide Learn about rate limits
Best Practices SDK best practices
API Reference Complete API documentation
Error Codes Reference All error codes