Error Handling
The Fanfare SDK uses a structured error system with specific error codes and types. This page documents all error types and provides patterns for handling them effectively.FanfareError
All SDK errors extend theFanfareError base class:
Copy
class FanfareError extends Error {
code: ErrorCode;
statusCode?: number;
details?: Record<string, unknown>;
retryable: boolean;
constructor(options: FanfareErrorOptions);
}
interface FanfareErrorOptions {
code: ErrorCode;
message: string;
statusCode?: number;
details?: Record<string, unknown>;
retryable?: boolean;
cause?: Error;
}
Error Codes
ErrorCodes Constant
Copy
const ErrorCodes = {
// Configuration errors
INVALID_CONFIG: "INVALID_CONFIG",
MISSING_ORGANIZATION_ID: "MISSING_ORGANIZATION_ID",
MISSING_PUBLISHABLE_KEY: "MISSING_PUBLISHABLE_KEY",
// Authentication errors
UNAUTHORIZED: "UNAUTHORIZED",
INVALID_OTP: "INVALID_OTP",
SESSION_EXPIRED: "SESSION_EXPIRED",
AUTHENTICATION_REQUIRED: "AUTHENTICATION_REQUIRED",
INVALID_TOKEN: "INVALID_TOKEN",
// Network errors
NETWORK_ERROR: "NETWORK_ERROR",
TIMEOUT: "TIMEOUT",
SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE",
// Queue errors
QUEUE_NOT_FOUND: "QUEUE_NOT_FOUND",
QUEUE_CLOSED: "QUEUE_CLOSED",
QUEUE_FULL: "QUEUE_FULL",
ALREADY_IN_QUEUE: "ALREADY_IN_QUEUE",
NOT_IN_QUEUE: "NOT_IN_QUEUE",
// Draw errors
DRAW_NOT_FOUND: "DRAW_NOT_FOUND",
DRAW_CLOSED: "DRAW_CLOSED",
DRAW_FULL: "DRAW_FULL",
ALREADY_IN_DRAW: "ALREADY_IN_DRAW",
NOT_IN_DRAW: "NOT_IN_DRAW",
DRAW_NOT_STARTED: "DRAW_NOT_STARTED",
// Auction errors
AUCTION_NOT_FOUND: "AUCTION_NOT_FOUND",
AUCTION_CLOSED: "AUCTION_CLOSED",
AUCTION_NOT_STARTED: "AUCTION_NOT_STARTED",
BID_TOO_LOW: "BID_TOO_LOW",
INVALID_BID_AMOUNT: "INVALID_BID_AMOUNT",
RESERVE_NOT_MET: "RESERVE_NOT_MET",
// Experience errors
EXPERIENCE_NOT_FOUND: "EXPERIENCE_NOT_FOUND",
SEQUENCE_NOT_FOUND: "SEQUENCE_NOT_FOUND",
ACCESS_CODE_REQUIRED: "ACCESS_CODE_REQUIRED",
INVALID_ACCESS_CODE: "INVALID_ACCESS_CODE",
NO_SEQUENCE_AVAILABLE: "NO_SEQUENCE_AVAILABLE",
// Waitlist errors
WAITLIST_NOT_FOUND: "WAITLIST_NOT_FOUND",
WAITLIST_CLOSED: "WAITLIST_CLOSED",
// Timed release errors
TIMED_RELEASE_NOT_FOUND: "TIMED_RELEASE_NOT_FOUND",
TIMED_RELEASE_NOT_STARTED: "TIMED_RELEASE_NOT_STARTED",
TIMED_RELEASE_ENDED: "TIMED_RELEASE_ENDED",
// General errors
VALIDATION_ERROR: "VALIDATION_ERROR",
RATE_LIMITED: "RATE_LIMITED",
INTERNAL_ERROR: "INTERNAL_ERROR",
UNKNOWN_ERROR: "UNKNOWN_ERROR",
} as const;
type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
Deny Reasons
Queue Deny Reasons
When a consumer is denied entry to a queue:Copy
const QueueDenyReason = {
ORDER_LIMIT_EXCEEDED: "ORDER_LIMIT_EXCEEDED",
CAPACITY_EXCEEDED: "CAPACITY_EXCEEDED",
VALIDATION_FAILED: "VALIDATION_FAILED",
AUDIENCE_MISMATCH: "AUDIENCE_MISMATCH",
QUEUE_CLOSED: "QUEUE_CLOSED",
ADMISSION_EXPIRED: "ADMISSION_EXPIRED",
} as const;
Draw Deny Reasons
When a consumer is denied entry to a draw:Copy
const DrawDenyReason = {
ORDER_LIMIT_EXCEEDED: "ORDER_LIMIT_EXCEEDED",
CAPACITY_EXCEEDED: "CAPACITY_EXCEEDED",
VALIDATION_FAILED: "VALIDATION_FAILED",
AUDIENCE_MISMATCH: "AUDIENCE_MISMATCH",
DRAW_CLOSED: "DRAW_CLOSED",
NOT_SELECTED: "NOT_SELECTED",
} as const;
Error Handling Patterns
Basic Error Handling
Copy
import { FanfareError, ErrorCodes } from "@waitify-io/fanfare-sdk-core";
try {
await fanfare.queues.enter("queue_123");
} catch (error) {
if (error instanceof FanfareError) {
console.error("Fanfare error:", error.code, error.message);
} else {
console.error("Unexpected error:", error);
}
}
Switch on Error Code
Copy
try {
await fanfare.queues.enter("queue_123");
} catch (error) {
if (error instanceof FanfareError) {
switch (error.code) {
case ErrorCodes.QUEUE_CLOSED:
showMessage("This queue is currently closed.");
break;
case ErrorCodes.QUEUE_FULL:
showMessage("This queue is full. Please try again later.");
break;
case ErrorCodes.ALREADY_IN_QUEUE:
showMessage("You are already in this queue.");
break;
case ErrorCodes.AUTHENTICATION_REQUIRED:
showLoginModal();
break;
default:
showMessage(`Error: ${error.message}`);
}
}
}
Authentication Errors
Copy
try {
await fanfare.auth.verifyOtp({
email: "[email protected]",
code: userInputCode,
});
} catch (error) {
if (error instanceof FanfareError) {
switch (error.code) {
case ErrorCodes.INVALID_OTP:
showMessage("Invalid code. Please check and try again.");
break;
case ErrorCodes.SESSION_EXPIRED:
showMessage("Code expired. Please request a new one.");
await fanfare.auth.requestOtp({ email: "[email protected]" });
break;
default:
showMessage("Authentication failed. Please try again.");
}
}
}
Network Error Handling
Copy
try {
await fanfare.queues.enter("queue_123");
} catch (error) {
if (error instanceof FanfareError) {
if (error.code === ErrorCodes.NETWORK_ERROR || error.code === ErrorCodes.TIMEOUT) {
// Retry with exponential backoff
if (error.retryable) {
await retryWithBackoff(() => fanfare.queues.enter("queue_123"));
}
}
}
}
async function retryWithBackoff(fn: () => Promise<unknown>, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise((r) => setTimeout(r, Math.pow(2, i) * 1000));
}
}
}
Auction Bid Errors
Copy
try {
await fanfare.auctions.placeBid("auction_123", "150.00");
} catch (error) {
if (error instanceof FanfareError) {
switch (error.code) {
case ErrorCodes.BID_TOO_LOW:
const minBid = error.details?.minBid as string;
showMessage(`Bid must be at least ${minBid}`);
break;
case ErrorCodes.AUCTION_CLOSED:
showMessage("This auction has ended.");
break;
case ErrorCodes.RESERVE_NOT_MET:
showMessage("Reserve price not met.");
break;
default:
showMessage("Failed to place bid. Please try again.");
}
}
}
Experience Journey Errors
Copy
try {
await journey.start({ accessCode: userAccessCode });
} catch (error) {
if (error instanceof FanfareError) {
switch (error.code) {
case ErrorCodes.INVALID_ACCESS_CODE:
showMessage("Invalid access code. Please check and try again.");
break;
case ErrorCodes.ACCESS_CODE_REQUIRED:
showAccessCodeInput();
break;
case ErrorCodes.NO_SEQUENCE_AVAILABLE:
showMessage("No available access for this experience.");
break;
case ErrorCodes.EXPERIENCE_NOT_FOUND:
showMessage("Experience not found.");
break;
default:
showMessage("Failed to start experience.");
}
}
}
Event-Based Error Handling
Subscribe to error events for async error handling:Copy
// Queue errors
fanfare.on("queue:error", ({ queueId, error }) => {
if (error instanceof FanfareError && error.code === ErrorCodes.QUEUE_CLOSED) {
showNotification("The queue has closed.");
updateUI("closed");
}
});
// Draw errors
fanfare.on("draw:error", ({ drawId, error }) => {
showNotification(`Draw error: ${error.message}`);
});
// Auction errors
fanfare.on("auction:error", ({ auctionId, error }) => {
showNotification(`Auction error: ${error.message}`);
});
Custom Error Handling Utility
Copy
import { FanfareError, ErrorCodes } from "@waitify-io/fanfare-sdk-core";
type ErrorMessages = Partial<Record<ErrorCode, string>>;
function handleFanfareError(error: unknown, customMessages?: ErrorMessages): string {
if (!(error instanceof FanfareError)) {
return "An unexpected error occurred. Please try again.";
}
// Check for custom message first
if (customMessages?.[error.code]) {
return customMessages[error.code]!;
}
// Default messages
const defaultMessages: ErrorMessages = {
[ErrorCodes.NETWORK_ERROR]: "Network error. Please check your connection.",
[ErrorCodes.TIMEOUT]: "Request timed out. Please try again.",
[ErrorCodes.UNAUTHORIZED]: "Please log in to continue.",
[ErrorCodes.RATE_LIMITED]: "Too many requests. Please wait a moment.",
[ErrorCodes.SERVICE_UNAVAILABLE]: "Service temporarily unavailable.",
};
return defaultMessages[error.code] || error.message;
}
// Usage
try {
await fanfare.queues.enter("queue_123");
} catch (error) {
const message = handleFanfareError(error, {
[ErrorCodes.QUEUE_FULL]: "This queue is currently full. Check back soon!",
});
showToast(message);
}
Error Details
Some errors include additional details:Copy
try {
await fanfare.auctions.placeBid("auction_123", "50.00");
} catch (error) {
if (error instanceof FanfareError) {
console.log("Code:", error.code);
console.log("Message:", error.message);
console.log("Status:", error.statusCode); // HTTP status if applicable
console.log("Details:", error.details);
// details might include: { minBid: "100.00", currentHighest: "95.00" }
console.log("Retryable:", error.retryable);
}
}
Typed Error Checking
Copy
import { FanfareError, ErrorCodes, type ErrorCode } from "@waitify-io/fanfare-sdk-core";
function isAuthError(error: unknown): error is FanfareError {
const authCodes: ErrorCode[] = [
ErrorCodes.UNAUTHORIZED,
ErrorCodes.SESSION_EXPIRED,
ErrorCodes.AUTHENTICATION_REQUIRED,
ErrorCodes.INVALID_TOKEN,
];
return error instanceof FanfareError && authCodes.includes(error.code);
}
try {
await fanfare.queues.enter("queue_123");
} catch (error) {
if (isAuthError(error)) {
redirectToLogin();
}
}