Skip to content

Standardize SDK exceptions to use specific, Pythonic exception subclasses #4131

@radisicc

Description

@radisicc

Description

The SDK should raise specific, semantically meaningful exception subclasses (e.g. DaytonaNotFoundError, DaytonaAuthenticationError) instead of the generic DaytonaError for distinct failure modes. This lets consumers use idiomatic try/except patterns instead of string-parsing error messages.

Problem

Today the SDK inconsistently raises the base DaytonaError for situations where a more specific subclass already exists or should exist. For example, calling sandbox.fs.download_file() on a path that doesn't exist sometimes raises DaytonaNotFoundError and sometimes raises the base DaytonaError with a message containing "file not found."

This forces consumers to write brittle workarounds like:

try:
    data = await sandbox.fs.download_file(path)
except DaytonaNotFoundError:
    return {}
except DaytonaError as exc:
    # SDK sometimes raises the base class instead of DaytonaNotFoundError
    if "file not found" in str(exc).lower():
        return {}
    raise

The string-matching fallback is fragile — it breaks if the server-side message changes wording, is localized, or includes additional context. Idiomatic Python expects distinct exception types for distinct failure categories.

Proposal

Audit every code path in the SDK that raises DaytonaError and map each to a specific subclass. Where a subclass doesn't exist yet, create one.

Suggested exception hierarchy:

DaytonaError (base — keep as the catch-all parent)
├── DaytonaNotFoundError          # 404 / resource doesn't exist
├── DaytonaAuthenticationError    # 401 / invalid or missing API key
├── DaytonaAuthorizationError     # 403 / insufficient permissions
├── DaytonaRateLimitError         # 429 / rate limit exceeded
├── DaytonaConflictError          # 409 / resource already exists or state conflict
├── DaytonaValidationError        # 400 / bad input, malformed request
├── DaytonaTimeoutError           # request or operation timed out
└── DaytonaConnectionError        # network-level failure (DNS, TLS, etc.)

DX & UX Implementation:

  • Interface: No CLI changes. SDK consumers switch from string-matching except DaytonaError blocks to specific except DaytonaNotFoundError blocks. Existing except DaytonaError catch-alls continue to work since all subclasses inherit from it — this is fully backward-compatible.
  • Feedback: Each exception should carry structured attributes (status_code, error_code, message) so consumers can programmatically react without parsing strings. Example:
    except DaytonaNotFoundError as exc:
        print(exc.status_code)   # 404
        print(exc.error_code)    # "FILE_NOT_FOUND"
        print(exc.message)       # "File '/workspace/manifest.json' does not exist"
  • Configuration: No configuration changes needed.

Consistency across SDKs: If the TypeScript SDK has the same issue, the same hierarchy and error codes should be adopted there to keep the multi-language experience consistent.

Acceptance Criteria

  • Every API/server error is mapped to a specific exception subclass based on HTTP status code and error type
  • File-system operations (download_file, upload_file, list_dir, etc.) raise DaytonaNotFoundError — not the base DaytonaError — when a path doesn't exist
  • Authentication failures raise DaytonaAuthenticationError (related: API should return Unauthorized for wrong API keys #1772)
  • All exception subclasses inherit from DaytonaError (backward-compatible)
  • Exceptions expose structured attributes (status_code, error_code, message)
  • TypeScript SDK exception classes are consistent with the Python hierarchy
  • SDK reference docs list all exception types with example usage
  • Existing except DaytonaError blocks in consumer code continue to work without changes

Impact

Eliminates a class of fragile string-parsing workarounds across every codebase that uses the Daytona SDK. Consumers get idiomatic, reliable error handling that won't break when message text changes. This is a foundational DX improvement — predictable exceptions are table stakes for a production-grade SDK.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions