Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/iii-hq/sdk/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The III SDK provides structured error handling across function invocations, WebSocket connections, and trigger registrations. Understanding error types and handling patterns ensures robust distributed applications.

Error Types

Invocation Errors

When calling remote functions, the SDK returns structured error objects:
// Error structure from InvocationResultMessage
type InvocationError = {
  code: 'invocation_failed' | 'function_not_found'
  message: string
}

// Timeout error
try {
  await iii.call('my-function', { data: 'value' }, 5000)
} catch (error) {
  // Error: Invocation timeout after 5000ms: my-function
  console.error(error.message)
}

Registration Errors

// Duplicate function ID
try {
  iii.registerFunction({ id: 'users::get' }, handler)
  iii.registerFunction({ id: 'users::get' }, handler) // throws
} catch (error) {
  // Error: function id already registered: users::get
}

// Missing required fields
try {
  iii.registerFunction({ id: '' }, handler)
} catch (error) {
  // Error: id is required
}

Trigger Registration Errors

// Trigger handler errors are communicated via TriggerRegistrationResult
iii.registerTriggerType(
  { id: 'cron', description: 'Cron trigger' },
  {
    registerTrigger: async (config) => {
      if (!config.config.schedule) {
        throw new Error('schedule is required')
      }
      // Error propagated to engine as:
      // { code: 'trigger_registration_failed', message: 'schedule is required' }
    },
    unregisterTrigger: async (config) => {},
  }
)

Function Handler Error Handling

Try/Catch in Handlers

1

Wrap handler logic in try/catch

Catch errors inside your function handlers to return meaningful responses:
import { getContext } from 'iii-sdk'

iii.registerFunction({ id: 'users::create' }, async (input) => {
  const { logger } = getContext()
  
  try {
    // Validate input
    if (!input.email) {
      logger.warn('Missing email field', { input })
      return { error: 'email is required' }
    }
    
    // Perform operation
    const user = await db.createUser(input)
    logger.info('User created', { userId: user.id })
    return { success: true, user }
    
  } catch (error) {
    logger.error('Failed to create user', { error: error.message, input })
    
    // Return structured error response
    if (error.code === 'DUPLICATE_KEY') {
      return { error: 'User already exists' }
    }
    
    return { error: 'Internal server error' }
  }
})
2

Log errors with context

Use the context logger to attach trace IDs and span IDs automatically:
iii.registerFunction({ id: 'process::payment' }, async (input) => {
  const { logger, trace } = getContext()
  
  try {
    const result = await stripe.charge(input.amount)
    return result
  } catch (error) {
    // Automatically includes trace_id, span_id, function_id
    logger.error('Payment failed', { 
      amount: input.amount,
      stripeError: error.code 
    })
    
    // Add error details to span
    trace?.setStatus({ code: SpanStatusCode.ERROR, message: error.message })
    trace?.recordException(error)
    
    throw error // Re-throw to return error to caller
  }
})

Uncaught Errors

If a handler throws an uncaught error, the SDK automatically catches it and returns an error response to the caller:
iii.registerFunction({ id: 'divide' }, async ({ a, b }) => {
  return a / b // Will throw if not numbers
})

// Caller receives:
// {
//   error: {
//     code: 'invocation_failed',
//     message: 'Cannot read property of undefined'
//   }
// }

Invocation Error Handling

Configuring Timeouts

import { init, DEFAULT_INVOCATION_TIMEOUT_MS } from 'iii-sdk'

// Set default timeout for all invocations
const iii = init('ws://localhost:49134', {
  invocationTimeoutMs: 10000 // 10 seconds
})

// Override per-invocation
try {
  const result = await iii.call('slow-function', data, 30000) // 30 seconds
} catch (error) {
  if (error.message.includes('timeout')) {
    console.error('Function took too long')
  }
}

Handling Connection Errors

import type { ConnectionStateCallback } from 'iii-sdk'

const handleConnectionState: ConnectionStateCallback = (state) => {
  switch (state) {
    case 'connected':
      console.log('Ready to invoke functions')
      break
    case 'reconnecting':
      console.warn('Connection lost, retrying...')
      break
    case 'failed':
      console.error('Max retries reached, connection failed')
      // Implement fallback or alert
      break
  }
}

iii.onConnectionStateChange(handleConnectionState)

Best Practices

Error Handling Checklist:
  • ✅ Always validate input at the start of handlers
  • ✅ Use structured error responses (not just strings)
  • ✅ Log errors with context using ctx.logger.error()
  • ✅ Set appropriate timeouts for long-running operations
  • ✅ Handle connection state changes for resilience
  • ✅ Record exceptions in spans for distributed tracing
  • ✅ Differentiate between client errors (4xx) and server errors (5xx)

Graceful Degradation

iii.registerFunction({ id: 'get-recommendations' }, async (input) => {
  const { logger } = getContext()
  
  try {
    // Try ML service first
    return await iii.call('ml::recommend', input, 5000)
  } catch (error) {
    logger.warn('ML service unavailable, using fallback', { error: error.message })
    
    // Fallback to simple logic
    return await iii.call('db::popular-items', { limit: 10 })
  }
})

Shutdown Error Handling

// Graceful shutdown rejects pending invocations
process.on('SIGTERM', async () => {
  console.log('Shutting down...')
  await iii.shutdown() // Rejects all pending with 'iii is shutting down'
  process.exit(0)
})

Build docs developers (and LLMs) love