Reference

Error Handling

Exception types and error handling in the MyoSapiens SDK.

The MyoSapiens SDK uses typed exceptions to help you handle errors gracefully. All exceptions inherit from APIError.

Exception Hierarchy

APIError (base exception)
├── AuthenticationError (401)
├── NotFoundError (404)
├── ValidationError (400, 422)
├── RateLimitError (429)
└── ServerError (500-599)

Exception Types

APIError

Base exception for all API errors.

from myosdk.exceptions import ApiError

try:
    asset = client.assets.get("invalid-id")
except ApiError as e:
    print(f"Error: {e.message}")
    print(f"Status code: {e.status_code}")
    print(f"Response data: {e.response_data}")

Attributes:

  • message (str) - Error message
  • status_code (int | None) - HTTP status code
  • response_data (dict | None) - Full error response from API

AuthenticationError

Raised when authentication fails (401 Unauthorized).

from myosdk.exceptions import AuthenticationError

try:
    client = Client(api_key="invalid_key")
    assets = client.assets.list()
except AuthenticationError as e:
    print("Authentication failed. Check your API key.")

Common causes:

  • Invalid or expired API key
  • Missing API key
  • API key revoked

Solution:

  • Verify your API key at dev.myolab.ai
  • Ensure you're using the correct API key format
  • Check that your API key hasn't been revoked

NotFoundError

Raised when a resource is not found (404 Not Found).

from myosdk.exceptions import NotFoundError

try:
    asset = client.assets.get("non-existent-id")
except NotFoundError as e:
    print(f"Asset not found: {e.message}")

Common causes:

  • Invalid asset ID, job ID, or character ID
  • Resource was deleted
  • Resource belongs to a different tenant

Solution:

  • Verify the resource ID is correct
  • Check that the resource exists and belongs to your tenant
  • Use list() methods to find valid IDs

ValidationError

Raised when request validation fails (400 Bad Request, 422 Unprocessable Entity).

from myosdk.exceptions import ValidationError

try:
    result = client.retarget(
        tracker="invalid",
        markerset="invalid",
    )
except ValidationError as e:
    print(f"Validation error: {e.message}")
    print(f"Details: {e.response_data}")

Common causes:

  • Missing required parameters
  • Invalid parameter values
  • Asset has wrong purpose for the operation
  • Asset upload not completed

Solution:

  • Check that all required parameters are provided
  • Verify parameter types and values
  • Ensure assets are in the correct state (e.g., upload completed)

RateLimitError

Raised when rate limit is exceeded (429 Too Many Requests).

from myosdk.exceptions import RateLimitError
import time

try:
    for i in range(1000):
        assets = client.assets.list()
except RateLimitError as e:
    print(f"Rate limit exceeded: {e.message}")
    if e.retry_after:
        print(f"Retry after {e.retry_after} seconds")
        time.sleep(e.retry_after)

Attributes:

  • retry_after (int | None) - Seconds to wait before retrying (from Retry-After header)

Common causes:

  • Too many requests in a short time period
  • Exceeding your plan's rate limits

Solution:

  • Implement exponential backoff
  • Use the retry_after value to wait before retrying
  • Consider upgrading your plan for higher rate limits
  • Batch operations when possible

ServerError

Raised when server returns a 5xx error.

from myosdk.exceptions import ServerError

try:
    job = client.jobs.get(job_id)
except ServerError as e:
    print(f"Server error: {e.message}")
    print(f"Status code: {e.status_code}")
    # Retry with exponential backoff

Common causes:

  • Temporary server issues
  • Service overload
  • Internal server errors

Solution:

  • Implement retry logic with exponential backoff
  • The SDK automatically retries on server errors (up to 3 times)
  • If errors persist, contact [email protected]

Error Handling Patterns

Basic Error Handling

from myosdk import Client
from myosdk.exceptions import ApiError, NotFoundError, ValidationError

try:
    asset = client.assets.get(asset_id)
except NotFoundError:
    print("Asset not found")
except ValidationError as e:
    print(f"Invalid request: {e.message}")
except ApiError as e:
    print(f"API error: {e.message}")
except Exception as e:
    print(f"Unexpected error: {e}")

Retry with Exponential Backoff

import time
from myosdk.exceptions import RateLimitError, ServerError

def retry_with_backoff(func, max_retries=3, initial_delay=1.0):
    """Retry a function with exponential backoff."""
    for attempt in range(max_retries):
        try:
            return func()
        except (RateLimitError, ServerError) as e:
            if attempt == max_retries - 1:
                raise

            # Use retry_after if available, otherwise use exponential backoff
            if isinstance(e, RateLimitError) and e.retry_after:
                delay = e.retry_after
            else:
                delay = initial_delay * (2 ** attempt)

            print(f"Retrying after {delay} seconds...")
            time.sleep(delay)

# Usage
asset = retry_with_backoff(lambda: client.assets.get(asset_id))

Handling Job Failures

from myosdk.exceptions import ApiError, JobFailedError, JobTimeoutError

try:
    result = client.retarget(
        tracker="motion.c3d",
        markerset="markerset.xml",
        timeout=300,
    )
    result.download_all("out/")
    print("Job completed successfully!")

except JobTimeoutError:
    print("Job did not complete within timeout period")
except JobFailedError as e:
    print(f"Job failed: {e}")
except ApiError as e:
    print(f"Error: {e.message}")

Validating Before Operations

from pathlib import Path
from myosdk.exceptions import NotFoundError, ValidationError

def safe_retarget(tracker, markerset):
    """Run a retarget job with validation."""
    try:
        # If using asset IDs (not file paths), validate they exist
        if isinstance(tracker, str) and not Path(tracker).exists():
            client.assets.get(tracker)
        if isinstance(markerset, str) and not Path(markerset).exists():
            client.assets.get(markerset)
    except NotFoundError as e:
        print(f"Asset not found: {e.message}")
        return None

    try:
        return client.retarget(tracker=tracker, markerset=markerset)
    except ValidationError as e:
        print(f"Invalid job parameters: {e.message}")
        return None

Best Practices

  1. Always handle exceptions - Don't let API errors crash your application
  2. Use specific exception types - Catch specific exceptions for better error handling
  3. Implement retries - For transient errors (rate limits, server errors), implement retry logic
  4. Log errors - Log error details for debugging and monitoring
  5. Validate inputs - Check asset IDs and parameters before making requests
  6. Check job status - Always check job status before accessing output

Example: Complete Error Handling

import os
from pathlib import Path
from myosdk import Client
from myosdk.exceptions import (
    ApiError,
    AuthenticationError,
    InsufficientCreditsError,
    JobFailedError,
    JobTimeoutError,
    NotFoundError,
    ValidationError,
)

def process_retarget(tracker_path, markerset_path, output_dir="out/"):
    """Complete retarget workflow with comprehensive error handling."""
    try:
        client = Client(api_key=os.getenv("MYO_API_KEY"))

        try:
            result = client.retarget(
                tracker=tracker_path,
                markerset=markerset_path,
                export_glb=True,
                stream_status=True,
                output_dir=output_dir,
                timeout=600,
            )
            print(f"Job completed! Processing time: {result.processing_time_seconds}s")
            if not output_dir:
                result.download_all("out/")
            print("Download complete!")
            return "out/"
        except InsufficientCreditsError as e:
            print(f"Insufficient credits: {e}")
            return None
        except JobTimeoutError:
            print("Job did not complete within timeout period")
            return None
        except JobFailedError as e:
            print(f"Job failed: {e}")
            return None
        except ValidationError as e:
            print(f"Validation error: {e.message}")
            return None
        except ApiError as e:
            print(f"API error: {e.message}")
            return None

    except AuthenticationError:
        print("Authentication failed. Check your API key.")
        return None
    except Exception as e:
        print(f"Unexpected error: {e}")
        return None
    finally:
        client.close()

Next Steps