Skip to content

Error Handling

Comprehensive guide to handling errors in the Urban Sky SDK.

Error Types

The error event delivers a payload with two fields — there is no numeric or symbolic error code on it. Branch on message (and on error for the reconnect sentinel).

FieldTypeDescription
messagestringWhat the SDK was doing when it failed, e.g. Authentication failed
errorstringThe underlying cause, stringified. Also the literal RECONNECT_FAILED sentinel

Messages the SDK emits

messageWhen it happensRecovery strategy
Authentication failedToken rejected during auth/refreshVerify the API token
Max reconnection attempts (N) reachedReconnect budget exhaustedRecreate the SDK, check the network
All reconnection attempts failedEvery backoff attempt erroredRecreate the SDK, check the network
Subscription errorChannel subscription rejectedVerify token scopes
Failed to process publicationA received message could not be parsedLog and continue
Failed to process balloon updateA balloon update could not be parsedLog and continue
Failed to process unassigned devices updateAn unassigned-devices message failedLog and continue

In JavaScript the same payload arrives as { message, error }; the exact strings are identical across both languages.

Basic Error Handling

Connection Errors

Handle connection failures gracefully:

javascript
const sdk = await UrbanSkySDK.init({
  apiToken: 'your-token',
  baseUrl: 'https://api.ops.atmosys.com',
})

// Handle connection errors. The payload is { message, error } — no code field.
sdk.on('error', ({ message, error }) => {
  console.error('SDK Error:', message, '-', error)

  if (message === 'Authentication failed') {
    console.error('❌ Authentication failed. Verify your API token.')
  } else if (error === 'RECONNECT_FAILED') {
    console.error('❌ Reconnect budget exhausted. Recreate the SDK.')
  } else if (message.startsWith('All reconnection attempts failed')) {
    console.log('🔄 Reconnection failed. Check your network.')
  } else if (message === 'Subscription error') {
    console.error('🚫 Subscription rejected. Check your token scopes.')
  } else {
    // Parse failures ("Failed to process ...") are non-fatal — log and continue.
    console.warn('❓ Non-fatal SDK error:', message)
  }
})

console.log('✅ SDK initialized and connected successfully')
python
# Load the SDK (requires `pip install requests`)
# The SDK also requires centrifuge-python: pip install centrifuge-python
import requests
exec(requests.get('https://sdk.atmosys.com/runtime/py/current/loader.py').text)

sdk = await UrbanSkySDK.init({
    'apiToken': 'your-token',
    'baseUrl': 'https://api.ops.atmosys.com'
})

# The error payload is a dict: {'message': ..., 'error': ...}
def on_error(payload):
    message = payload['message']
    cause = payload['error']
    print(f"SDK Error: {message} - {cause}")

    if message == 'Authentication failed':
        print("❌ Authentication failed. Verify your API token.")

    elif cause == 'RECONNECT_FAILED':
        print("❌ Reconnect budget exhausted. Recreate the SDK.")

    elif message.startswith('All reconnection attempts failed'):
        print("🔄 Reconnection failed. Check your network.")

    elif message == 'Subscription error':
        print("🚫 Subscription rejected. Check your token scopes.")

    else:
        # Parse failures ("Failed to process ...") are non-fatal - log and continue.
        print(f"❓ Non-fatal SDK error: {message}")

sdk.on('error', on_error)

Handling Disconnections

The SDK automatically attempts to reconnect when disconnected. You can monitor the connection status:

javascript
// Track connection state
let isConnected = false

sdk.on('connected', () => {
  isConnected = true
  console.log('✅ Connected to Urban Sky')
})

sdk.on('disconnected', () => {
  isConnected = false
  console.log('❌ Disconnected from Urban Sky')
})

// Check connection before operations
function sendData(data) {
  if (!isConnected) {
    console.warn('Not connected. Data will be queued.')
    return
  }

  // Process data
}
python
# Track connection state
is_connected = False

def on_connected(_):
    global is_connected
    is_connected = True
    print("✅ Connected to Urban Sky")

def on_disconnected(_):
    global is_connected
    is_connected = False
    print("❌ Disconnected from Urban Sky")

sdk.on('connected', on_connected)
sdk.on('disconnected', on_disconnected)

# Check connection before operations
def send_data(data):
    if not is_connected:
        print("Not connected. Data will be queued.")
        return

    # Process data

Error Recovery Patterns

Retry Logic

Implement simple retry logic for transient errors:

javascript
async function connectWithRetry(maxAttempts = 3) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      await sdk.connect()
      console.log('✅ Connected successfully')
      return true
    } catch (error) {
      console.error(`Attempt ${attempt} failed: ${error.message}`)

      if (attempt < maxAttempts) {
        const delay = attempt * 2000 // Exponential backoff
        console.log(`Retrying in ${delay}ms...`)
        await new Promise(resolve => setTimeout(resolve, delay))
      }
    }
  }

  console.error('❌ All connection attempts failed')
  return false
}

// Usage
const connected = await connectWithRetry()
if (!connected) {
  console.error('Unable to establish connection')
}
python
import asyncio

async def connect_with_retry(max_attempts=3):
    for attempt in range(1, max_attempts + 1):
        try:
            await sdk.connect()
            print("✅ Connected successfully")
            return True
        except Exception as error:
            print(f"Attempt {attempt} failed: {error}")

            if attempt < max_attempts:
                delay = attempt * 2  # Exponential backoff
                print(f"Retrying in {delay} seconds...")
                await asyncio.sleep(delay)

    print("❌ All connection attempts failed")
    return False

# Usage
connected = await connect_with_retry()
if not connected:
    print("Unable to establish connection")

Handling Data Processing Errors

Implement error boundaries for data processing:

javascript
sdk.on('balloon:update', update => {
  try {
    // Process update
    processUpdate(update)
  } catch (error) {
    console.error('Error processing update:', error)
    // Log error but don't crash the application
    logError(error, update)
  }
})

function processUpdate(update) {
  // Validate data
  if (!update.balloonId || !update.devices) {
    throw new Error('Invalid update structure')
  }

  // Process each device safely
  update.devices.forEach(device => {
    try {
      processDevice(device)
    } catch (error) {
      console.error(`Error processing device ${device.deviceId}:`, error)
    }
  })
}
python
def on_balloon_update(update):
    try:
        # Process update
        process_update(update)
    except Exception as error:
        print(f"Error processing update: {error}")
        # Log error but don't crash the application
        log_error(error, update)

def process_update(update):
    # Validate data
    if not hasattr(update, 'balloon_id') or not hasattr(update, 'devices'):
        raise ValueError("Invalid update structure")

    # Process each device safely
    for device in update.devices:
        try:
            process_device(device)
        except Exception as error:
            print(f"Error processing device {device.device_id}: {error}")

sdk.on('balloon:update', on_balloon_update)

Best Practices

  1. Always implement error handlers - Don't let errors crash your application
  2. Log errors appropriately - Include context for debugging
  3. Use the SDK's automatic reconnection - Don't implement your own unless necessary
  4. Handle authentication errors differently - These require user action
  5. Implement data validation - Verify data structure before processing

Getting Help

If you encounter persistent errors:

  1. Check the error code and message
  2. Verify your API token is valid
  3. Ensure you have a stable internet connection
  4. Contact Urban Sky support at support@atmosys.com with:
    • Your organization name
    • The error code and message
    • When the error occurred
    • Any relevant context about what you were trying to do