MeetingRouter

Error Handling in v1 API

@meeting-baas/sdk - v1 API Reference / v1-error-handling

Error Handling in v1 API

The v1 API uses a discriminated union response type that makes error handling type-safe and straightforward.

Response Structure

type ApiResponse\<T\> =
  | { success: true; data: T; error?: never }
  | { success: false; error: ZodError | Error; data?: never }

Error Types

Validation Errors (ZodError)

Parameter validation failures return a ZodError:

import { ZodError } from "zod";

const result = await client.joinMeeting({
  meeting_url: "not-a-valid-url",
  bot_name: "Test Bot",
  reserved: false
});

if (!result.success) {
  if (result.error instanceof ZodError) {
    console.error("Validation failed:");

    result.error.errors.forEach(err => {
      console.error(`  Field: ${err.path.join(".")}`);
      console.error(`  Error: ${err.message}`);
      console.error(`  Received: ${err.code}`);
    });
  }
}

API Errors (Error)

API-level errors return a standard Error object:

const result = await client.joinMeeting({
  meeting_url: "https://meet.google.com/abc-def-ghi",
  bot_name: "Test Bot",
  reserved: false
});

if (!result.success && !(result.error instanceof ZodError)) {
  console.error("API error:", result.error.message);

  // Parse error details if available
  try {
    const errorData = JSON.parse(result.error.message);
    console.error("Status:", errorData.status);
    console.error("Details:", errorData.data);
  } catch {
    // Not JSON, just a regular error message
  }
}

Common Error Scenarios

Invalid Meeting URL

const result = await client.joinMeeting({
  meeting_url: "https://invalid-platform.com/meeting",
  bot_name: "Test Bot",
  reserved: false
});

if (!result.success) {
  if (result.error instanceof ZodError) {
    const urlError = result.error.errors.find(
      err => err.path.includes("meeting_url")
    );

    if (urlError) {
      console.error("Invalid meeting URL format");
    }
  } else {
    console.error("Meeting URL not supported");
  }
}

Authentication Errors

const client = createBaasClient({
  api_key: "invalid-key"
});

const result = await client.joinMeeting({ ... });

if (!result.success) {
  if (result.error.message.includes("401") ||
      result.error.message.includes("Unauthorized")) {
    console.error("Invalid API key. Please check your credentials.");
  }
}

Rate Limiting

const result = await client.joinMeeting({ ... });

if (!result.success) {
  if (result.error.message.includes("429") ||
      result.error.message.includes("rate limit")) {
    console.error("Rate limit exceeded. Please retry after some time.");

    // Implement exponential backoff
    await new Promise(resolve => setTimeout(resolve, 5000));
    // Retry...
  }
}

Not Found Errors

const result = await client.getMeetingData({
  bot_id: "non-existent-id"
});

if (!result.success) {
  if (result.error.message.includes("404") ||
      result.error.message.includes("not found")) {
    console.error("Bot not found. It may have been deleted.");
  }
}

Error Handling Patterns

Basic Pattern

async function safeBotOperation\<T\>(
  operation: () => Promise<{ success: boolean; data?: T; error?: any }>
): Promise<T | null> {
  const result = await operation();

  if (result.success) {
    return result.data;
  }

  console.error("Operation failed:", result.error);
  return null;
}

// Usage
const botData = await safeBotOperation(() =>
  client.getMeetingData({ bot_id: "bot-123" })
);

if (botData) {
  console.log("Bot status:", botData.status_changes);
}

Retry Pattern

async function withRetry\<T\>(
  operation: () => Promise<{ success: boolean; data?: T; error?: any }>,
  maxRetries = 3
): Promise\<T\> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const result = await operation();

    if (result.success) {
      return result.data!;
    }

    const isLastAttempt = attempt === maxRetries;
    const shouldRetry = !(result.error instanceof ZodError) &&
                       (result.error.message.includes("429") ||
                        result.error.message.includes("5"));

    if (!shouldRetry || isLastAttempt) {
      throw result.error;
    }

    const delay = Math.pow(2, attempt) * 1000;
    console.log(`Retry ${attempt}/${maxRetries} after ${delay}ms`);
    await new Promise(resolve => setTimeout(resolve, delay));
  }

  throw new Error("Max retries exceeded");
}

// Usage
try {
  const result = await withRetry(() =>
    client.joinMeeting({
      meeting_url: "https://meet.google.com/abc",
      bot_name: "Test Bot",
      reserved: false
    })
  );

  console.log("Bot ID:", result.bot_id);
} catch (error) {
  console.error("Failed after retries:", error);
}

Type-Safe Error Handling

function handleError(error: ZodError | Error): { type: string; message: string } {
  if (error instanceof ZodError) {
    return {
      type: "validation",
      message: error.errors.map(e => `${e.path.join(".")}: ${e.message}`).join(", ")
    };
  }

  // Parse API errors
  if (error.message.includes("401")) {
    return { type: "auth", message: "Authentication failed" };
  }

  if (error.message.includes("429")) {
    return { type: "rate_limit", message: "Too many requests" };
  }

  if (error.message.includes("404")) {
    return { type: "not_found", message: "Resource not found" };
  }

  return { type: "unknown", message: error.message };
}

// Usage
const result = await client.joinMeeting({ ... });

if (!result.success) {
  const errorInfo = handleError(result.error);

  switch (errorInfo.type) {
    case "validation":
      console.error("Invalid parameters:", errorInfo.message);
      break;

    case "auth":
      console.error("Authentication error:", errorInfo.message);
      // Refresh credentials
      break;

    case "rate_limit":
      console.error("Rate limited:", errorInfo.message);
      // Implement backoff
      break;

    default:
      console.error("Error:", errorInfo.message);
  }
}

Logging Best Practices

Development Logging

function logError(operation: string, error: ZodError | Error) {
  console.error(`[${operation}] Error occurred`);

  if (error instanceof ZodError) {
    console.error("Validation errors:");
    error.errors.forEach(err => {
      console.error(`  - ${err.path.join(".")}: ${err.message}`);
    });
  } else {
    console.error("Message:", error.message);
    console.error("Stack:", error.stack);
  }
}

// Usage
const result = await client.joinMeeting({ ... });
if (!result.success) {
  logError("joinMeeting", result.error);
}

Production Logging

interface ErrorLog {
  timestamp: string;
  operation: string;
  errorType: "validation" | "api";
  message: string;
  details?: any;
}

function logErrorToMonitoring(log: ErrorLog) {
  // Send to your monitoring service
  console.error(JSON.stringify(log));
}

const result = await client.joinMeeting({ ... });

if (!result.success) {
  logErrorToMonitoring({
    timestamp: new Date().toISOString(),
    operation: "joinMeeting",
    errorType: result.error instanceof ZodError ? "validation" : "api",
    message: result.error.message,
    details: result.error instanceof ZodError ? result.error.errors : undefined
  });
}

User-Friendly Error Messages

function getUserMessage(error: ZodError | Error): string {
  if (error instanceof ZodError) {
    const field = error.errors[0]?.path.join(".") || "input";
    return `Invalid ${field}. Please check your input and try again.`;
  }

  if (error.message.includes("401")) {
    return "Authentication failed. Please check your API key.";
  }

  if (error.message.includes("429")) {
    return "Too many requests. Please try again in a few moments.";
  }

  if (error.message.includes("404")) {
    return "The requested resource was not found.";
  }

  if (error.message.includes("5")) {
    return "Server error. Please try again later.";
  }

  return "An unexpected error occurred. Please try again.";
}

// Usage in UI
const result = await client.joinMeeting({ ... });

if (!result.success) {
  alert(getUserMessage(result.error));
}

Complete Error Handling Example

import { createBaasClient } from "@meeting-baas/sdk";
import { ZodError } from "zod";

const client = createBaasClient({
  api_key: process.env.MEETING_BAAS_API_KEY!
});

async function joinMeetingSafely(meetingUrl: string, botName: string) {
  try {
    const result = await client.joinMeeting({
      meeting_url: meetingUrl,
      bot_name: botName,
      reserved: true
    });

    if (result.success) {
      return {
        ok: true,
        botId: result.data.bot_id,
        message: "Bot joined successfully"
      };
    }

    // Handle validation errors
    if (result.error instanceof ZodError) {
      const errors = result.error.errors.map(e =>
        `${e.path.join(".")}: ${e.message}`
      );

      return {
        ok: false,
        reason: "validation_error",
        message: "Invalid parameters",
        details: errors
      };
    }

    // Handle API errors
    const errorMessage = result.error.message;

    if (errorMessage.includes("401")) {
      return {
        ok: false,
        reason: "auth_error",
        message: "Invalid API key"
      };
    }

    if (errorMessage.includes("429")) {
      return {
        ok: false,
        reason: "rate_limit",
        message: "Rate limit exceeded"
      };
    }

    return {
      ok: false,
      reason: "api_error",
      message: errorMessage
    };

  } catch (error) {
    // Network errors, timeouts, etc.
    console.error("Unexpected error:", error);

    return {
      ok: false,
      reason: "network_error",
      message: "Failed to connect to Meeting BaaS API"
    };
  }
}

// Usage
const result = await joinMeetingSafely(
  "https://meet.google.com/abc-def-ghi",
  "My Bot"
);

if (result.ok) {
  console.log("Success:", result.message);
  console.log("Bot ID:", result.botId);
} else {
  console.error("Failed:", result.message);

  if (result.reason === "validation_error") {
    console.error("Validation errors:", result.details);
  }
}

On this page