Skip to content

Troubleshooting

Common issues and solutions when using the MsGine SDK and API.

Authentication Issues

Error: "Invalid or missing API token"

Symptoms:

  • HTTP 401 status code
  • Error code: unauthorized

Causes:

  1. API token is incorrect or missing
  2. Token has been revoked
  3. Token not properly included in request headers

Solutions:

Check Token Format

typescript
// ✅ Correct
const client = new MsGineClient({
  apiToken: process.env.MSGINE_API_TOKEN!
})

// ❌ Incorrect - missing Bearer prefix in manual requests
fetch('https://api.msgine.net/api/v1/messages/sms', {
  headers: {
    'Authorization': 'YOUR_TOKEN' // Missing "Bearer " prefix
  }
})

// ✅ Correct format for manual requests
fetch('https://api.msgine.net/api/v1/messages/sms', {
  headers: {
    'Authorization': `Bearer ${process.env.MSGINE_API_TOKEN}`
  }
})

Verify Token

bash
curl https://api.msgine.net/api/v1/account \
  -H "Authorization: Bearer YOUR_API_TOKEN"

If this fails, generate a new token in the Developer Dashboard.

Check Environment Variables

typescript
// Add debugging
console.log('API Token:', process.env.MSGINE_API_TOKEN ? 'Set' : 'Missing')

if (!process.env.MSGINE_API_TOKEN) {
  throw new Error('MSGINE_API_TOKEN environment variable is not set')
}

Error: "Insufficient permissions"

Symptoms:

  • HTTP 403 status code
  • Error code: forbidden

Solutions:

  1. Check token scopes: Ensure your token has the required permissions
  2. Generate new token: Create a token with appropriate scopes in the dashboard
  3. Contact support: If you need additional permissions

Phone Number Issues

Error: "Invalid phone number"

Symptoms:

  • HTTP 400 status code
  • Error code: invalid_phone_number

Common Mistakes:

typescript
// ❌ Missing country code
to: '0701521269'

// ❌ Missing + prefix
to: '256701521269'

// ❌ Has spaces or dashes
to: '+256 701 521 269'
to: '+256-701-521-269'

// ✅ Correct E.164 format
to: '+256701521269'

Solution:

typescript
function formatPhoneNumber(phone: string): string {
  // Remove all non-digit characters except +
  let cleaned = phone.replace(/[^\d+]/g, '')

  // Add + if missing
  if (!cleaned.startsWith('+')) {
    // Assuming country code needs to be added
    throw new Error('Please include country code with + prefix')
  }

  return cleaned
}

// Usage
const phone = formatPhoneNumber('+256 701 521 269')
// Returns: '+256701521269'

Messages Not Delivering

Symptoms:

  • Message status remains pending or changes to failed
  • No error during sending

Possible Causes:

  1. Invalid phone number: Number doesn't exist or is invalid
  2. Network issues: Carrier network problems
  3. Phone is off: Recipient's phone is turned off
  4. Blocked number: Number is on a blocklist

Solutions:

Check Message Status

typescript
const result = await client.sendSms({
  to: '+256701521269',
  message: 'Hello!'
})

// Wait a few seconds
await new Promise(resolve => setTimeout(resolve, 5000))

const status = await client.getMessageStatus(result.id)
console.log('Status:', status.status)

if (status.status === 'failed') {
  console.log('Failure reason:', status.failureReason)
}

Use Webhooks for Status Updates

typescript
await client.sendSms({
  to: '+256701521269',
  message: 'Hello!',
  callbackUrl: 'https://myapp.com/webhook'
})

// Webhook handler
app.post('/webhook', (req, res) => {
  const { id, status, failureReason } = req.body

  if (status === 'failed') {
    console.log(`Message ${id} failed: ${failureReason}`)
  }

  res.status(200).send()
})

Rate Limiting Issues

Error: "Rate limit exceeded"

Symptoms:

  • HTTP 429 status code
  • Error code: rate_limit_exceeded

Solutions:

Implement Retry with Backoff

The SDK handles this automatically:

typescript
const client = new MsGineClient({
  apiToken: process.env.MSGINE_API_TOKEN!,
  retryConfig: {
    maxRetries: 3,
    initialDelayMs: 1000,
    backoffMultiplier: 2
  }
})

Use Request Queuing

typescript
class MessageQueue {
  private queue: Array<() => Promise<any>> = []
  private processing = false
  private readonly delayMs = 600 // 100 requests/min = 600ms between requests

  async enqueue<T>(fn: () => Promise<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      this.queue.push(async () => {
        try {
          const result = await fn()
          resolve(result)
        } catch (error) {
          reject(error)
        }
      })

      if (!this.processing) {
        this.process()
      }
    })
  }

  private async process() {
    this.processing = true

    while (this.queue.length > 0) {
      const fn = this.queue.shift()!
      await fn()
      await new Promise(resolve => setTimeout(resolve, this.delayMs))
    }

    this.processing = false
  }
}

// Usage
const queue = new MessageQueue()

for (const phone of phones) {
  await queue.enqueue(() =>
    client.sendSms({ to: phone, message: 'Hello!' })
  )
}

Check Rate Limit Headers

typescript
// When using REST API directly
const response = await fetch('https://api.msgine.net/api/v1/messages/sms', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.MSGINE_API_TOKEN}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ to: phone, message: text })
})

const remaining = response.headers.get('X-RateLimit-Remaining')
const reset = response.headers.get('X-RateLimit-Reset')

console.log(`Remaining: ${remaining}`)
console.log(`Resets at: ${new Date(parseInt(reset!) * 1000)}`)

Network and Timeout Issues

Error: "Network request failed"

Symptoms:

  • Error code: network_error
  • Request times out

Solutions:

Increase Timeout

typescript
const client = new MsGineClient({
  apiToken: process.env.MSGINE_API_TOKEN!,
  timeoutMs: 60000 // Increase to 60 seconds
})

Check Network Connectivity

bash
# Test connection to API
curl https://api.msgine.net/api/v1/account \
  -H "Authorization: Bearer YOUR_API_TOKEN"

# Check DNS resolution
nslookup api.msgine.net

# Check firewall rules
# Ensure outbound HTTPS (port 443) is allowed

Verify Proxy Settings

typescript
// If using a proxy
import { HttpsProxyAgent } from 'https-proxy-agent'

const agent = new HttpsProxyAgent(process.env.HTTP_PROXY!)

// Configure your HTTP client to use the proxy

SDK Installation Issues

Error: "Cannot find module '@msgine/sdk'"

Solutions:

Reinstall the Package

bash
# Using pnpm
pnpm add @msgine/sdk

# Using npm
npm install @msgine/sdk

# Using yarn
yarn add @msgine/sdk

Check package.json

Ensure the package is listed in dependencies:

json
{
  "dependencies": {
    "@msgine/sdk": "^1.0.0"
  }
}

Clear Node Modules

bash
rm -rf node_modules package-lock.json
npm install

TypeScript Type Errors

Solutions:

Ensure TypeScript Version

bash
npm install -D typescript@latest

Check tsconfig.json

json
{
  "compilerOptions": {
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true
  }
}

Balance and Billing Issues

Error: "Insufficient balance"

Symptoms:

  • HTTP 402 status code
  • Error code: insufficient_balance

Solutions:

  1. Add funds: Visit the Billing Dashboard
  2. Check pricing: Review pricing for your destination
  3. Set up auto-reload: Configure automatic balance reload

Monitor Balance

typescript
async function checkBalanceAndAlert() {
  const account = await client.getAccount()

  console.log(`Balance: ${account.balance} ${account.currency}`)

  if (account.balance < 10) {
    console.warn('Low balance! Please add funds.')
    // Send alert
  }
}

// Check before sending
await checkBalanceAndAlert()

Webhook Issues

Webhooks Not Being Received

Symptoms:

  • Webhook handler never called
  • No POST requests to webhook URL

Solutions:

Verify Webhook URL

bash
# Test webhook URL is accessible
curl https://your-app.com/webhooks/msgine

Check HTTPS

MsGine only sends to HTTPS URLs:

typescript
// ❌ Will be rejected
callbackUrl: 'http://myapp.com/webhook'

// ✅ Correct
callbackUrl: 'https://myapp.com/webhook'

Use ngrok for Local Testing

bash
# Install ngrok
npm install -g ngrok

# Start ngrok tunnel
ngrok http 3000

# Use ngrok URL as webhook URL
# https://abc123.ngrok.io/webhooks/msgine

Check Firewall

Ensure MsGine's IP addresses aren't blocked:

52.89.214.238
34.212.75.30
54.218.53.128

Webhook Signature Verification Fails

Symptoms:

  • Signature verification returns false
  • Webhook requests rejected

Solutions:

Check Webhook Secret

typescript
// Verify you're using the correct webhook secret
const secret = process.env.MSGINE_WEBHOOK_SECRET!
console.log('Secret:', secret ? 'Set' : 'Missing')

Use Raw Request Body

typescript
import express from 'express'

const app = express()

// Use raw body for signature verification
app.post(
  '/webhooks/msgine',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['x-msgine-signature'] as string
    const payload = req.body.toString()

    const expectedSignature = crypto
      .createHmac('sha256', process.env.MSGINE_WEBHOOK_SECRET!)
      .update(payload)
      .digest('hex')

    if (signature === expectedSignature) {
      // Signature valid
      const event = JSON.parse(payload)
      // Process event
    }

    res.status(200).send()
  }
)

Message Content Issues

Messages Appearing Garbled

Symptoms:

  • Special characters display incorrectly
  • Emojis show as question marks

Solutions:

Use UTF-8 Encoding

typescript
// Ensure UTF-8 encoding
const message = 'Hello! 👋'

await client.sendSms({
  to: '+256701521269',
  message
})

Avoid Problematic Characters

Some characters may not be supported by all carriers:

typescript
function cleanMessage(message: string): string {
  // Remove unsupported control characters
  return message
    .replace(/[\x00-\x1F\x7F]/g, '')
    .trim()
}

Message Split into Multiple Parts

Cause: Message exceeds single SMS length (160 characters for GSM-7, 70 for Unicode)

Solution:

typescript
function estimateMessageParts(message: string): number {
  // Simple estimation
  const hasUnicode = /[^\x00-\x7F]/.test(message)
  const singleLength = hasUnicode ? 70 : 160
  const partLength = hasUnicode ? 67 : 153

  if (message.length <= singleLength) {
    return 1
  }

  return Math.ceil(message.length / partLength)
}

const message = 'Your long message here...'
const parts = estimateMessageParts(message)
console.log(`This message will be sent as ${parts} parts`)

Getting More Help

Enable Debug Logging

typescript
const client = new MsGineClient({
  apiToken: process.env.MSGINE_API_TOKEN!,
  onError: (error) => {
    console.error('MsGine Error:', {
      code: error.code,
      message: error.message,
      statusCode: error.statusCode,
      details: error.details
    })
  }
})

Check API Status

Visit the Status Page to check for service incidents.

Contact Support

If you're still experiencing issues:

  1. Email: support@msgine.net
  2. Include:
    • Error message
    • Message ID (if applicable)
    • Timestamp
    • Code snippet (remove sensitive data)
  3. Response time: Usually within 24 hours

Next Steps

Released under the MIT License.