Error Handling
The Moitribe SDK provides structured error handling with status codes and error messages to help you handle failures gracefully.
Error Response Structure
All SDK callbacks receive responses with a consistent error structure:
interface ErrorResponse {
success: boolean; // Always false for errors
code: number; // Error status code
message: string; // Human-readable error message
[key: string]: any; // Additional error data
}
Basic Error Handling Pattern
MoitribeSDK('my-game', 'getprofile', {}, (result) => {
if (result.success) {
// Handle success
console.log('Profile loaded:', result.data);
} else {
// Handle error
console.error('Error loading profile:', result.message);
console.error('Status code:', result.code);
}
});
Status Codes
The SDK provides comprehensive status codes through the StatusCodes enum:
import { StatusCodes } from '@veniso/moitribe-js';
// Common status codes
console.log(StatusCodes.STATUS_OK); // 0
console.log(StatusCodes.STATUS_INTERNAL_ERROR); // 1
console.log(StatusCodes.STATUS_SIGNED_IN_PLAYER_REQUIRED); // -6
General Status Codes
| Code | Constant | Description |
|---|---|---|
| 0 | STATUS_OK | Operation successful |
| 1 | STATUS_INTERNAL_ERROR | Internal server error |
| 2 | STATUS_INCORRECT_GAMEID | Invalid game ID |
| -3 | STATUS_API_OBSOLETE | API version obsolete |
| -4 | STATUS_API_SERVICE_IS_DOWN | Service unavailable |
| -5 | STATUS_INVALID_API_VERSION | Invalid API version |
| -6 | STATUS_SIGNED_IN_PLAYER_REQUIRED | Authentication required |
| -7 | STATUS_SIGNED_DELETED_PLAYER | Player account deleted |
Profile Status Codes
| Code | Constant | Description |
|---|---|---|
| 13001 | STATUS_PROFILE_EMAIL_PHONENO_EXISTS | Email/phone already exists |
| 13002 | STATUS_PROFILE_INSUFFICIENT_DETAILS | Insufficient profile details |
| 13003 | STATUS_PROFILE_ACCOUNT_NOT_FOUND | Account not found |
| 13004 | STATUS_PROFILE_NO_DATA_FOR_UPDATE | No data to update |
| 13005 | STATUS_PROFILE_OLD_PASSWORD_MISMATCH | Old password incorrect |
| 13006 | STATUS_PROFILE_INVALID_CREDENTIALS | Invalid credentials |
| 13007 | STATUS_PROFILE_UNSUPPORTED_SOCIAL_PLATFORM | Unsupported social platform |
| 13008 | STATUS_PROFACTION_VERIFY_NONE | Verification required |
| 13009 | STATUS_INVALID_PLAYER_ID | Invalid player ID |
| 13010 | STATUS_INVALID_LOGIN_OPTION | Invalid login option |
| 13011 | STATUS_PROFACTION_OTP_PARAM_INVALID | Invalid OTP parameters |
| 13012 | STATUS_PROFACTION_OTP_INCORRECT | Incorrect OTP |
| 13013 | STATUS_PROFACTION_OTP_UNAVAILABLE | OTP service unavailable |
| 13014 | STATUS_PROFACTION_CALL_VERIFY_FIRST | Verification required first |
Leaderboard Status Codes
| Code | Constant | Description |
|---|---|---|
| 12001 | STATUS_LEADERBOARD_SUBMIT_SCORE_FAILED | Score submission failed |
| 12002 | STATUS_LEADERBOARD_SCORE_ZERO | Score cannot be zero |
Real-Time Multiplayer Status Codes
| Code | Constant | Description |
|---|---|---|
| 7002 | STATUS_INVALID_REAL_TIME_ROOM_ID | Invalid room ID |
| 7005 | STATUS_REAL_TIME_INACTIVE_ROOM | Room is inactive |
| 7008 | STATUS_REAL_TIME_NO_SLOT | No available slots |
| 7009 | STATUS_REAL_TIME_SIZE_ZERO | Room size is zero |
| 7010 | STATUS_REAL_TIME_PLAYER_ALREADY_IN_ROOM | Player already in room |
Tournament Status Codes
| Code | Constant | Description |
|---|---|---|
| 15001 | STATUS_TOURNAMENT_NOT_FOUND | Tournament not found |
| 15002 | STATUS_TOURNAMENT_OUT_OF_DATE | Tournament data outdated |
| 15003 | STATUS_TOURNAMENT_PARTICIPANTS_FULL | Tournament full |
| 15004 | STATUS_TOURNAMENT_VERIFIED_REQUIRED | Verification required |
| 15005 | STATUS_TOURNAMENT_NOT_JOINED | Not joined tournament |
| 15006 | STATUS_TOURNAMENT_SEND_SUBTOURNAMENT_ID | Sub-tournament ID required |
| 15007 | STATUS_TOURNAMENT_ALREADY_JOINED | Already joined tournament |
| 15008 | STATUS_TOURNAMENT_NO_ID_SENT | No tournament ID sent |
| 15009 | STATUS_TOURNAMENT_NO_CLAIMS | No tournament claims |
Advanced Error Handling
Centralized Error Handler
Create a centralized error handler for consistent error management:
import { StatusCodes } from '@veniso/moitribe-js';
interface SDKResult<T = any> {
success: boolean;
data?: T;
code?: number;
message?: string;
}
class ErrorHandler {
static handle<T>(result: SDKResult<T>, onSuccess?: (data: T) => void, onError?: (error: { code: number; message: string }) => void): void {
if (result.success && result.data) {
onSuccess?.(result.data);
} else {
const error = {
code: result.code || StatusCodes.STATUS_INTERNAL_ERROR,
message: result.message || 'Unknown error occurred'
};
onError?.(error);
// Log error for debugging
console.error('SDK Error:', error);
}
}
static isAuthenticationError(code: number): boolean {
return [
StatusCodes.STATUS_SIGNED_IN_PLAYER_REQUIRED,
StatusCodes.STATUS_SIGNED_DELETED_PLAYER,
StatusCodes.STATUS_PROFILE_INVALID_CREDENTIALS
].includes(code);
}
static isNetworkError(code: number): boolean {
return [
StatusCodes.STATUS_API_SERVICE_IS_DOWN,
StatusCodes.STATUS_API_OBSOLETE,
StatusCodes.STATUS_INVALID_API_VERSION
].includes(code);
}
static getErrorMessage(code: number): string {
const errorMessages: Record<number, string> = {
[StatusCodes.STATUS_SIGNED_IN_PLAYER_REQUIRED]: 'Player authentication required',
[StatusCodes.STATUS_PROFILE_INVALID_CREDENTIALS]: 'Invalid login credentials',
[StatusCodes.STATUS_LEADERBOARD_SUBMIT_SCORE_FAILED]: 'Failed to submit score',
[StatusCodes.STATUS_REAL_TIME_NO_SLOT]: 'No available slots in room',
[StatusCodes.STATUS_TOURNAMENT_FULL]: 'Tournament is full'
};
return errorMessages[code] || `Error code: ${code}`;
}
}
// Usage
MoitribeSDK('my-game', 'getprofile', {}, (result) => {
ErrorHandler.handle(
result,
(profile) => {
console.log('Profile loaded:', profile.name);
},
(error) => {
if (ErrorHandler.isAuthenticationError(error.code)) {
// Redirect to login
console.log('Authentication required, redirecting to login...');
} else {
console.error('Operation failed:', ErrorHandler.getErrorMessage(error.code));
}
}
);
});
Retry Logic
Implement retry logic for transient errors:
class RetryHandler {
static async executeWithRetry<T>(
operation: () => Promise<SDKResult<T>>,
maxRetries: number = 3,
delay: number = 1000
): Promise<SDKResult<T>> {
let lastResult: SDKResult<T>;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
lastResult = await operation();
if (lastResult.success) {
return lastResult;
}
// Don't retry on authentication or validation errors
if (ErrorHandler.isAuthenticationError(lastResult.code || 0)) {
return lastResult;
}
if (attempt < maxRetries) {
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
delay *= 2; // Exponential backoff
}
} catch (error) {
console.error(`Attempt ${attempt} threw error:`, error);
if (attempt === maxRetries) {
throw error;
}
}
}
return lastResult!;
}
}
// Usage with callback wrapper
const executeSDKWithRetry = <T>(
gameId: string,
method: string,
params: any,
callback: (result: SDKResult<T>) => void,
maxRetries: number = 3
) => {
const operation = () => new Promise<SDKResult<T>>((resolve) => {
MoitribeSDK(gameId, method, params, resolve);
});
RetryHandler.executeWithRetry(operation, maxRetries)
.then(callback)
.catch(error => {
callback({
success: false,
code: StatusCodes.STATUS_INTERNAL_ERROR,
message: error.message
});
});
};
Error Boundaries
Create error boundaries for different operations:
class ProfileManager {
static loadProfile(gameId: string, callback: (profile?: SignedInProfile, error?: string) => void): void {
MoitribeSDK(gameId, 'getprofile', {}, (result) => {
if (result.success) {
callback(result.data);
} else {
let errorMessage = 'Failed to load profile';
switch (result.code) {
case StatusCodes.STATUS_SIGNED_IN_PLAYER_REQUIRED:
errorMessage = 'Please log in to view your profile';
break;
case StatusCodes.STATUS_PROFILE_ACCOUNT_NOT_FOUND:
errorMessage = 'Profile not found';
break;
case StatusCodes.STATUS_API_SERVICE_IS_DOWN:
errorMessage = 'Service temporarily unavailable';
break;
default:
errorMessage = result.message || errorMessage;
}
callback(undefined, errorMessage);
}
});
}
}
class LeaderboardManager {
static submitScore(
gameId: string,
leaderboardId: string,
score: number,
callback: (success: boolean, message?: string) => void
): void {
if (score <= 0) {
callback(false, 'Score must be greater than zero');
return;
}
MoitribeSDK(gameId, 'submitscore', {
leaderboardid: leaderboardId,
score: score
}, (result) => {
if (result.success) {
callback(true, 'Score submitted successfully');
} else {
let errorMessage = 'Failed to submit score';
switch (result.code) {
case StatusCodes.STATUS_LEADERBOARD_SCORE_ZERO:
errorMessage = 'Score cannot be zero';
break;
case StatusCodes.STATUS_LEADERBOARD_SUBMIT_SCORE_FAILED:
errorMessage = 'Score submission failed. Please try again.';
break;
default:
errorMessage = result.message || errorMessage;
}
callback(false, errorMessage);
}
});
}
}
Connection Error Handling
Handle connection-related errors with connection callbacks:
const connectionCallbacks = {
onConnectionLost: (errorCode: number, errorMsg: string) => {
console.error('Connection lost:', errorCode, errorMsg);
// Show user-friendly message
if (errorCode === StatusCodes.STATUS_API_SERVICE_IS_DOWN) {
alert('Service is temporarily unavailable. Please try again later.');
} else {
alert('Connection lost. Please check your internet connection.');
}
},
onConnected: (isReconnect: boolean, URI: string) => {
console.log('Connected to:', URI);
if (isReconnect) {
console.log('Reconnected successfully');
// Refresh data after reconnection
refreshGameData();
}
},
onSuccessConnect: () => {
console.log('Connection established');
},
onFailureConnect: () => {
console.error('Failed to establish connection');
alert('Failed to connect to game servers. Please try again.');
}
};
// Initialize with connection callbacks
MoitribeSDK('my-game', 'init', {
connectionCallbacks: connectionCallbacks
}, (result) => {
console.log('SDK initialized:', result.success);
});
Best Practices
1. Always Check Success Flag
// ✓ Good
MoitribeSDK('my-game', 'getprofile', {}, (result) => {
if (result.success) {
// Process success
} else {
// Handle error
}
});
// ✗ Bad
MoitribeSDK('my-game', 'getprofile', {}, (result) => {
// Process result without checking success
});
2. Provide User-Friendly Messages
const getUserFriendlyMessage = (code: number, technicalMessage: string): string => {
const userMessages: Record<number, string> = {
[StatusCodes.STATUS_SIGNED_IN_PLAYER_REQUIRED]: 'Please log in to continue',
[StatusCodes.STATUS_API_SERVICE_IS_DOWN]: 'Service temporarily unavailable',
[StatusCodes.STATUS_LEADERBOARD_SUBMIT_SCORE_FAILED]: 'Failed to save score. Please try again.'
};
return userMessages[code] || 'An error occurred. Please try again.';
};
3. Log Technical Details
MoitribeSDK('my-game', 'submitscore', { leaderboardid: 'scores', score: 1000 }, (result) => {
if (!result.success) {
// Log technical details for debugging
console.error('Score submission failed:', {
code: result.code,
message: result.message,
timestamp: new Date().toISOString(),
leaderboardId: 'scores',
score: 1000
});
// Show user-friendly message
alert(getUserFriendlyMessage(result.code, result.message));
}
});
Next Steps
- Callback Patterns - Understanding callback usage
- Connection Callbacks - Monitor connection state
- Logging - Debug with SDK logging
warning
Never expose technical error messages directly to users. Always provide user-friendly messages while logging technical details for debugging.