Skip to main content
from declaw import (
    SandboxException,
    TimeoutException,
    NotFoundException,
    AuthenticationException,
    InvalidArgumentException,
    NotEnoughSpaceException,
    TemplateException,
    BuildException,
    FileUploadException,
    GitAuthException,
    GitUpstreamException,
    CommandExitException,
    InsufficientBalanceException,
    RateLimitException,
)
All SDK exceptions inherit from SandboxException, which inherits from the built-in Exception. You can catch the base class to handle any Declaw error, or catch specific subclasses for granular handling.

Exception hierarchy

Exception
└── SandboxException
    ├── TimeoutException
    ├── NotFoundException
    ├── AuthenticationException
    ├── InvalidArgumentException
    ├── NotEnoughSpaceException
    ├── FileUploadException
    ├── GitAuthException
    ├── GitUpstreamException
    ├── CommandExitException
    ├── InsufficientBalanceException
    ├── RateLimitException
    └── TemplateException
        └── BuildException

SandboxException

Base class for all Declaw exceptions.
class SandboxException(Exception):
    def __init__(self, message: str = "", *, sandbox_id: str | None = None):
        self.sandbox_id = sandbox_id
        ...
Attributes
AttributeTypeDescription
sandbox_idstr | NoneThe sandbox ID involved in the error, when available.

TimeoutException

Raised when an operation exceeds its configured timeout.
from declaw import Sandbox, TimeoutException

try:
    result = sbx.commands.run("sleep 300", timeout=10)
except TimeoutException as e:
    print(f"Command timed out in sandbox {e.sandbox_id}")
Inherits sandbox_id from SandboxException.

NotFoundException

Raised when the sandbox or a requested resource does not exist (HTTP 404).
from declaw import Sandbox, NotFoundException

try:
    sbx = Sandbox.connect("nonexistent-id", api_key="key", domain="host:8080")
except NotFoundException as e:
    print("Sandbox not found:", e)

AuthenticationException

Raised when the API key is missing or invalid (HTTP 401/403).
from declaw import Sandbox, AuthenticationException

try:
    sbx = Sandbox.create(api_key="wrong-key", domain="host:8080")
except AuthenticationException:
    print("Invalid API key")

InvalidArgumentException

Raised when a method receives an argument that fails validation.
from declaw import NetworkPolicy, InvalidArgumentException

try:
    NetworkPolicy(allow_out=["not-a-valid-entry!!"])
except InvalidArgumentException as e:
    print("Bad argument:", e)

NotEnoughSpaceException

Raised when the sandbox filesystem is full and a write operation fails.
from declaw import NotEnoughSpaceException

try:
    sbx.files.write("/data/large_file.bin", large_bytes)
except NotEnoughSpaceException:
    print("Sandbox disk is full")

CommandExitException

Raised when a command exits with a non-zero exit code. Contains the full stdout, stderr, and exit code.
class CommandExitException(SandboxException):
    def __init__(
        self,
        message: str = "",
        *,
        exit_code: int = 1,
        stdout: str = "",
        stderr: str = "",
        sandbox_id: str | None = None,
    ):
        self.exit_code = exit_code
        self.stdout = stdout
        self.stderr = stderr
Attributes
AttributeTypeDescription
exit_codeintThe process exit code.
stdoutstrAccumulated stdout output.
stderrstrAccumulated stderr output.
from declaw import CommandExitException

try:
    result = sbx.commands.run("python3 -c \"raise ValueError('oops')\"")
except CommandExitException as e:
    print(f"Exit code: {e.exit_code}")
    print(f"Stderr: {e.stderr}")
CommandExitException is only raised by CommandHandle.wait(). The main sbx.commands.run() method returns a CommandResult with a non-zero exit_code rather than raising — you must check result.exit_code yourself unless you use a background handle.

TemplateException

Base class for template-related errors.

BuildException

Raised when a Template.build() call fails.
from declaw import BuildException

try:
    info = Template.build(template, "my-alias", api_key="key", domain="host:8080")
except BuildException as e:
    print("Build failed:", e)

FileUploadException

Raised when uploading a file to the sandbox fails (e.g. network error during multipart upload).

GitAuthException

Raised when git operations inside the sandbox fail due to authentication errors.

GitUpstreamException

Raised when git operations fail due to upstream repository errors.

InsufficientBalanceException

Raised when the account has insufficient balance to start or continue a sandbox operation (HTTP 402).
from declaw import Sandbox, InsufficientBalanceException

try:
    sbx = Sandbox.create(api_key="key", domain="host:8080")
except InsufficientBalanceException as e:
    print(f"Top up required for wallet: {e.wallet_type}")
Attributes
AttributeTypeDescription
wallet_typestrWhich wallet is out of balance (e.g. "sandbox", "guardrails").

RateLimitException

Raised when the account exceeds its rate limit (HTTP 429). Inspect retry_after and back off before retrying.
import time
from declaw import Sandbox, RateLimitException

try:
    sbx = Sandbox.create(api_key="key", domain="host:8080")
except RateLimitException as e:
    if e.retry_after:
        time.sleep(e.retry_after)
Attributes
AttributeTypeDescription
retry_afterfloat | NoneSeconds to wait before retrying, when provided by the server.
limitint | NoneConfigured request limit for the window.
remainingint | NoneRequests remaining in the current window.

Catching all Declaw errors

from declaw import SandboxException

try:
    sbx = Sandbox.create(api_key="key", domain="host:8080")
    result = sbx.commands.run("my-command")
except SandboxException as e:
    print(f"Declaw error: {e}")
    if e.sandbox_id:
        print(f"Sandbox ID: {e.sandbox_id}")
finally:
    sbx.kill()

Retry patterns

Simple retry with backoff

import time
from declaw import Sandbox, TimeoutException, SandboxException

def run_with_retry(cmd: str, retries: int = 3, delay: float = 1.0) -> str:
    sbx = Sandbox.create(api_key="key", domain="host:8080")
    try:
        for attempt in range(retries):
            try:
                result = sbx.commands.run(cmd, timeout=30)
                return result.stdout
            except TimeoutException:
                if attempt < retries - 1:
                    time.sleep(delay * (2 ** attempt))
                else:
                    raise
    finally:
        sbx.kill()

Retry with tenacity

from tenacity import retry, stop_after_attempt, wait_exponential
from declaw import Sandbox, TimeoutException

@retry(
    reraise=True,
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=2, max=10),
    retry=lambda e: isinstance(e, TimeoutException),
)
def run_command(sbx: Sandbox, cmd: str) -> str:
    result = sbx.commands.run(cmd, timeout=30)
    return result.stdout

Handle non-zero exit codes

from declaw import Sandbox

sbx = Sandbox.create(api_key="key", domain="host:8080")
try:
    result = sbx.commands.run("python3 risky_script.py")
    if result.exit_code != 0:
        print(f"Script failed (exit {result.exit_code})")
        print(f"Stderr: {result.stderr}")
    else:
        print(result.stdout)
finally:
    sbx.kill()

Async error handling

import asyncio
from declaw import AsyncSandbox, TimeoutException

async def main():
    sbx = await AsyncSandbox.create(api_key="key", domain="host:8080")
    try:
        result = await sbx.run_command("long-running-task", timeout=60)
        print(result.stdout)
    except TimeoutException:
        print("Timed out — killing sandbox")
    finally:
        await sbx.kill()

asyncio.run(main())