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:
- API token is incorrect or missing
- Token has been revoked
- Token not properly included in request headers
Solutions:
Check Token Format
// ✅ 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
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
// 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:
- Check token scopes: Ensure your token has the required permissions
- Generate new token: Create a token with appropriate scopes in the dashboard
- 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:
// ❌ 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:
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
pendingor changes tofailed - No error during sending
Possible Causes:
- Invalid phone number: Number doesn't exist or is invalid
- Network issues: Carrier network problems
- Phone is off: Recipient's phone is turned off
- Blocked number: Number is on a blocklist
Solutions:
Check Message Status
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
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:
const client = new MsGineClient({
apiToken: process.env.MSGINE_API_TOKEN!,
retryConfig: {
maxRetries: 3,
initialDelayMs: 1000,
backoffMultiplier: 2
}
})Use Request Queuing
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
// 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
const client = new MsGineClient({
apiToken: process.env.MSGINE_API_TOKEN!,
timeoutMs: 60000 // Increase to 60 seconds
})Check Network Connectivity
# 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 allowedVerify Proxy Settings
// 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 proxySDK Installation Issues
Error: "Cannot find module '@msgine/sdk'"
Solutions:
Reinstall the Package
# Using pnpm
pnpm add @msgine/sdk
# Using npm
npm install @msgine/sdk
# Using yarn
yarn add @msgine/sdkCheck package.json
Ensure the package is listed in dependencies:
{
"dependencies": {
"@msgine/sdk": "^1.0.0"
}
}Clear Node Modules
rm -rf node_modules package-lock.json
npm installTypeScript Type Errors
Solutions:
Ensure TypeScript Version
npm install -D typescript@latestCheck tsconfig.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:
- Add funds: Visit the Billing Dashboard
- Check pricing: Review pricing for your destination
- Set up auto-reload: Configure automatic balance reload
Monitor Balance
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
# Test webhook URL is accessible
curl https://your-app.com/webhooks/msgineCheck HTTPS
MsGine only sends to HTTPS URLs:
// ❌ Will be rejected
callbackUrl: 'http://myapp.com/webhook'
// ✅ Correct
callbackUrl: 'https://myapp.com/webhook'Use ngrok for Local Testing
# Install ngrok
npm install -g ngrok
# Start ngrok tunnel
ngrok http 3000
# Use ngrok URL as webhook URL
# https://abc123.ngrok.io/webhooks/msgineCheck Firewall
Ensure MsGine's IP addresses aren't blocked:
52.89.214.238
34.212.75.30
54.218.53.128Webhook Signature Verification Fails
Symptoms:
- Signature verification returns false
- Webhook requests rejected
Solutions:
Check Webhook Secret
// 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
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
// 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:
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:
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
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:
- Email: support@msgine.net
- Include:
- Error message
- Message ID (if applicable)
- Timestamp
- Code snippet (remove sensitive data)
- Response time: Usually within 24 hours
Next Steps
- Best Practices - Avoid common issues
- Error Handling - Handle errors gracefully
- Security Guide - Secure your integration