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 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 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 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 import ValidationError

try:
    job = client.jobs.start_retarget(
        c3d_asset_id="invalid",
        markerset_asset_id="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 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 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 support@myolab.ai

Error Handling Patterns

Basic Error Handling

from myosdk import Client, 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 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 import APIError

try:
    result = client.jobs.wait(job_id, timeout=300)

    if result["status"] == "FAILED":
        error = result.get("error", {})
        print(f"Job failed: {error.get('message', 'Unknown error')}")
        print(f"Error details: {error}")
    elif result["status"] == "SUCCEEDED":
        print("Job completed successfully!")
        output_asset_id = result["output"]["retarget_output_asset_id"]
        client.assets.download(output_asset_id, "output.npz")

except TimeoutError:
    print("Job did not complete within timeout period")
except APIError as e:
    print(f"Error checking job status: {e.message}")

Validating Before Operations

from myosdk import NotFoundError, ValidationError

def safe_start_retarget(c3d_asset_id, markerset_asset_id):
    """Start a retarget job with validation."""
    # Validate assets exist
    try:
        c3d_asset = client.assets.get(c3d_asset_id)
        if c3d_asset["status"] != "completed":
            raise ValidationError("C3D asset upload not completed")

        markerset_asset = client.assets.get(markerset_asset_id)
        if markerset_asset["status"] != "completed":
            raise ValidationError("Markerset asset upload not completed")
    except NotFoundError as e:
        print(f"Asset not found: {e.message}")
        return None

    # Start job
    try:
        return client.jobs.start_retarget(
            c3d_asset_id=c3d_asset_id,
            markerset_asset_id=markerset_asset_id
        )
    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
import time
from myosdk import (
    Client,
    APIError,
    AuthenticationError,
    NotFoundError,
    ValidationError,
    RateLimitError,
    ServerError
)

def process_retarget(c3d_path, markerset_path):
    """Complete retarget workflow with comprehensive error handling."""
    try:
        # Initialize client
        client = Client(api_key=os.getenv("MYOSDK_API_KEY"))

        # Upload C3D file
        try:
            c3d_asset = client.assets.upload_file(c3d_path)
            print(f"Uploaded C3D: {c3d_asset['asset_id']}")
        except ValidationError as e:
            print(f"C3D upload validation error: {e.message}")
            return None
        except APIError as e:
            print(f"C3D upload failed: {e.message}")
            return None

        # Upload markerset
        try:
            markerset_asset = client.assets.upload_file(markerset_path)
            print(f"Uploaded markerset: {markerset_asset['asset_id']}")
        except ValidationError as e:
            print(f"Markerset upload validation error: {e.message}")
            return None
        except APIError as e:
            print(f"Markerset upload failed: {e.message}")
            return None

        # Start retarget job
        try:
            job = client.jobs.start_retarget(
                c3d_asset_id=c3d_asset["asset_id"],
                markerset_asset_id=markerset_asset["asset_id"]
            )
            print(f"Started job: {job['job_id']}")
        except ValidationError as e:
            print(f"Job creation validation error: {e.message}")
            return None
        except APIError as e:
            print(f"Job creation failed: {e.message}")
            return None

        # Wait for completion
        try:
            result = client.jobs.wait(job["job_id"], timeout=600)

            if result["status"] == "FAILED":
                error = result.get("error", {})
                print(f"Job failed: {error.get('message', 'Unknown error')}")
                return None

            if result["status"] != "SUCCEEDED":
                print(f"Job ended with unexpected status: {result['status']}")
                return None

            # Download result
            output_asset_id = result["output"]["retarget_output_asset_id"]
            client.assets.download(output_asset_id, "output.npz")
            print("Download complete!")
            return "output.npz"

        except TimeoutError:
            print("Job did not complete within timeout period")
            return None
        except APIError as e:
            print(f"Error during job execution: {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