Handle failures and unexpected state

Symptom

Check or action

Authentication or profile error

Check the URL, token, and AI project; see Connection sources.

Unknown image tag

Import the image on the selected endpoint; see Import a Docker image.

A file from the previous command disappeared

For explicit ContreeSession, check that disposable=True was not requested. For a lazy sandbox, check whether an exception discarded unsaved work.

Work disappeared after Python restarted

Resume the same session_id from the same SQLite database.

Explicit async session has no image_uuid yet

Await ensure_ready() or a session operation before reading it.

ValueError when resuming

Supply a base image for a new tree, or node_id when existing history has multiple leaves.

A completed result did not become current

A newer local selection exists; select the saved result explicitly as shown below.

NotImplementedError from async sandbox sync methods

Use aexecute, aupload_files, or adownload_files.

Note

Async snippets with top-level await run inside an async function or a notebook that supports it. For a standalone script, use the asyncio.run(main()) structure from Your first sandbox.

Fail a sandbox workflow on a nonzero exit

A nonzero command exit returns a result. Calling run_workflow() below raises inside the context, discards unsaved changes, and closes the VM.

from contree_sdk import create_sandbox_session


def run_workflow() -> None:
    with create_sandbox_session(image="tag:alpine:latest") as sandbox:
        result = sandbox.run(shell="exit 7")
        if result.state.exit_code != 0:
            raise RuntimeError(f"Command failed with exit code {result.state.exit_code}")
from contree_sdk import create_sandbox_session


async def run_workflow() -> None:
    async with create_sandbox_session(image="tag:alpine:latest") as sandbox:
        result = await sandbox.run(shell="exit 7")
        if result.state.exit_code != 0:
            raise RuntimeError(f"Command failed with exit code {result.state.exit_code}")

Advanced: a command returned a nonzero exit code

This is a process result, so run() returns it without raising FailedOperationError. The example uses disposable execution and leaves the session image unchanged.

from contree_sdk import ContreeSession, create_sandbox_session


with create_sandbox_session(session_type=ContreeSession, image="tag:alpine:latest") as session:
    result = session.run(shell="echo failed >&2; exit 7", disposable=True)
    if result.state.exit_code != 0:
        print(result.stderr)
        print("exit:", result.state.exit_code)
from contree_sdk import ContreeAsyncSession, create_sandbox_session


async with create_sandbox_session(session_type=ContreeAsyncSession, image="tag:alpine:latest") as session:
    result = await session.run(shell="echo failed >&2; exit 7", disposable=True)
    if result.state.exit_code != 0:
        print(result.stderr)
        print("exit:", result.state.exit_code)

Commands save by default. Inspect the exit code before continuing. A saved command can have changed files even when its process exits nonzero. Roll back explicitly if those changes should not remain selected.

Advanced: select a late result

A completed operation retains its original parent. If local navigation or another operation changes the selected position, committing the result saves a sibling without selecting it. To save and select that result, give it a local branch name:

from contree_sdk import ContreeSession, create_sandbox_session


with create_sandbox_session(session_type=ContreeSession, image="tag:alpine:latest") as session:
    operation = session.spawn(shell="echo candidate > /value.txt")
    session.run(shell="echo newer > /value.txt")
    operation.wait()
    session.commit_result(operation, branch="recovered", title="Recovered result")
    print(session.image_uuid)
from contree_sdk import ContreeAsyncSession, create_sandbox_session


async with create_sandbox_session(session_type=ContreeAsyncSession, image="tag:alpine:latest") as session:
    operation = await session.spawn(shell="echo candidate > /value.txt")
    await session.run(shell="echo newer > /value.txt")
    await operation.wait()
    await session.commit_result(operation, branch="recovered", title="Recovered result")
    print(session.image_uuid)

Choose a fresh recovery branch name. The newer branch remains intact. The recovery branch selects the completed operation’s image; it does not merge the two filesystems.

Waiting failed, timed out, or was cancelled

Operation.wait() attempts cancellation when waiting raises and keeps the original exception. A cancelled operation raises InterruptedError; an operation with no successful result raises FailedOperationError. Client transport errors can also propagate. Cleanup is best effort: retain the operation UUID if you need to inspect its server status afterward.

A subprocess wait timeout does not itself terminate the whole operation. Use its operation’s signal() or cancel(), or leave the operation context to shut it down. See Advanced: control a running sandbox and SDK exceptions.

Advanced: catch an operation failure

If the server cannot complete the operation, the SDK raises FailedOperationError and leaves history unchanged. Keep the operation UUID to investigate the failure:

from contree_sdk import ContreeSession, create_sandbox_session
from contree_sdk.exceptions import FailedOperationError


with create_sandbox_session(session_type=ContreeSession, image="tag:alpine:latest") as session:
    try:
        session.run("echo", args=["hello"])
    except FailedOperationError as error:
        print("failed operation:", error.operation_uuid)
from contree_sdk import ContreeAsyncSession, create_sandbox_session
from contree_sdk.exceptions import FailedOperationError


async with create_sandbox_session(session_type=ContreeAsyncSession, image="tag:alpine:latest") as session:
    try:
        await session.run("echo", args=["hello"])
    except FailedOperationError as error:
        print("failed operation:", error.operation_uuid)