Skip to content

Error Handling

Properly handling errors is essential for building robust messaging applications with the MsGine SDK.

Error Types

The SDK throws different types of errors depending on the situation:

MsGineError

The base error class for all API errors:

typescript
import { MsGineError } from '@msgine/sdk'

try {
  await client.sms.send({
    to: '+256701521269',
    message: 'Hello!'
  })
} catch (error) {
  if (error instanceof MsGineError) {
    console.error('MsGine API Error:', error.message)
    console.error('Status Code:', error.statusCode)
    console.error('Error Code:', error.code)
  }
}

MsGineValidationError

Thrown when input fails validation before reaching the API:

typescript
import { MsGineValidationError } from '@msgine/sdk'

try {
  await client.sms.send({
    to: 'invalid-number',
    message: 'Hello!'
  })
} catch (error) {
  if (error instanceof MsGineValidationError) {
    console.error('Validation failed:', error.message)
    if (error.field) {
      console.error('Invalid field:', error.field)
    }
  }
}

Error Properties

ClassPropertyTypeDescription
MsGineErrormessagestringHuman-readable error description
MsGineErrorstatusCodenumberHTTP status code (e.g., 400, 401, 500)
MsGineErrorcodestringMachine-readable error code
MsGineValidationErrormessagestringValidation failure description
MsGineValidationErrorfieldstring?The field that failed validation

Common Error Codes

Authentication Errors

typescript
{
  code: 'unauthorized',
  statusCode: 401,
  message: 'Invalid or missing API key'
}

Solutions:

  • Verify your API key is correct
  • Check that the key hasn't been revoked
  • Ensure the key is properly loaded from environment variables

Validation Errors

typescript
{
  code: 'invalid_phone_number',
  statusCode: 400,
  message: 'The phone number must be in E.164 format',
  details: {
    field: 'to',
    value: '0701234567'
  }
}

Solutions:

  • Use E.164 format: +[country code][number]
  • Example: +256701521269 (not 0701521269)

Rate Limit Errors

typescript
{
  code: 'rate_limit_exceeded',
  statusCode: 429,
  message: 'Too many requests. Please try again later.',
  details: {
    retryAfter: 60
  }
}

Solutions:

  • Implement request queuing for high-volume applications
  • Respect the retryAfter value before retrying

Insufficient Balance

typescript
{
  code: 'insufficient_balance',
  statusCode: 402,
  message: 'Insufficient account balance'
}

Solutions:

Error Handling Best Practices

1. Always Use Try-Catch

typescript
async function sendMessage() {
  try {
    const result = await client.sms.send({
      to: '+256701521269',
      message: 'Hello!'
    })
    return result
  } catch (error) {
    if (error instanceof MsGineValidationError) {
      // Handle input validation errors
      console.error('Invalid input:', error.message, error.field)
    } else if (error instanceof MsGineError) {
      // Handle API errors
      console.error('API Error:', error.code, error.message)
    } else {
      // Handle unexpected errors
      console.error('Unexpected error:', error)
    }
    throw error
  }
}

2. Check Error Codes

typescript
try {
  await client.sms.send({ to: phone, message: text })
} catch (error) {
  if (error instanceof MsGineError) {
    switch (error.code) {
      case 'invalid_phone_number':
        console.error('Invalid phone:', error.details)
        break
      case 'rate_limit_exceeded':
        console.error('Rate limited, retry after:', error.details?.retryAfter)
        break
      case 'unauthorized':
        console.error('Authentication failed — check your API key')
        break
      case 'insufficient_balance':
        console.error('Low balance — top up at msgine.net/billing')
        break
      default:
        console.error('API error:', error.message)
    }
  }
}

3. Graceful Degradation

typescript
async function sendSmsWithFallback(to: string, message: string) {
  try {
    return await client.sms.send({ to, message })
  } catch (error) {
    if (error instanceof MsGineError) {
      // Log to monitoring service
      await logToMonitoring(error)

      // Attempt fallback notification method
      await sendEmailNotification(to, message)

      return null
    }
    throw error
  }
}

4. Retry on Transient Errors

typescript
async function sendWithRetry(to: string, message: string, maxAttempts = 3) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await client.sms.send({ to, message })
    } catch (error) {
      if (
        error instanceof MsGineError &&
        (error.statusCode === 429 || error.statusCode >= 500) &&
        attempt < maxAttempts
      ) {
        const delay = Math.pow(2, attempt) * 1000
        console.log(`Attempt ${attempt} failed, retrying in ${delay}ms`)
        await new Promise(resolve => setTimeout(resolve, delay))
      } else {
        throw error
      }
    }
  }
}

Validation Errors

The SDK validates inputs before making API requests. This catches obvious mistakes early:

typescript
try {
  await client.sms.send({
    to: 'not-a-phone-number', // Will throw MsGineValidationError
    message: 'Hello!'
  })
} catch (error) {
  if (error instanceof MsGineValidationError) {
    // Validation happened client-side — no API call was made
    console.error('Validation error:', error.message)
    if (error.field) {
      console.error('Problem field:', error.field)
    }
  }
}

Next Steps

Released under the MIT License.