---
title: "microvms: functions, constants, and exceptions"
description: The module-level functions, constants, and exceptions of the microvms Python package.
---

Everything `microvms` exports at module level that is not a class of its own: the functions, the constants, and the exception hierarchy. Import any of them with `from microvms import ...`.

## Functions

### `agent_constants`

```python
def agent_constants() -> dict
```

The layer's fixed values, for a caller that wants to reason about the guest.

### `build_unpriced_reason`

```python
def build_unpriced_reason() -> str
```

Why the image build has no price, as the reason that lands on the line item.

### `check_budget`

```python
def check_budget(report: CostReport, max_usd: str, on_breach: str) -> BudgetVerdict
```

Judges `report`'s total against a ceiling of `max_usd` (a decimal string, such as
`"1.50"`), with `on_breach` (`"warn"` or `"abort"`) saying what a breach does.

`on_breach` has no default, because a breach of a lower-bound total has already been
exceeded by an unknown margin, and whether that warns or refuses is the caller's call.
Core's `Budget::check`, the gate `microvm cost --max-cost` applies.

### `compare_residency`

```python
def compare_residency(
    size: SizeClass,
    hold_seconds: SupportsFloat | SupportsIndex,
    cycles: SupportsIndex = ...,
    *,
    rates: RateTable | None = None,
) -> ResidencyComparison
```

The warm-pool argument, with its own counter-argument attached.

Leave `cycles` out and the comparison prices one suspend/resume cycle, the core's default
and the one `microvm cost --compare` applies: the per-cycle figure is what keeps the
argument honest. A report's `suspend_resume_cycles` defaults to none instead.

### `core_version`

```python
def core_version() -> str
```

The core crate's version, for a `doctor` or `manifest` command to report.

The **core's** version and not this crate's: what a caller needs to know is which client
they are talking through, and a binding version that drifted from it would be a second
number nobody can act on.

### `cost_constants`

```python
def cost_constants() -> dict
```

The documented facts a caller may want to assert on, as strings and flags.

A dict rather than a class because it is a transcription of published constants, and
the Python client publishes the same set. `provenances` and `phases` are here so a
caller can enumerate the closed sets rather than hardcoding spellings.

### `egress_posture_for`

```python
def egress_posture_for(
    egress: bool = False,
    connectors: SupportsGetItem[int, str] | None = None,
    deny_egress: bool = False,
    *,
    region: Region | None = None,
) -> str
```

The egress posture `Sandbox.run` with these options would report, without launching.

One of `"open"`, `"unsealed"`, `"best-effort"`, or `"sealed"`: the value the launched
session's `egress_posture` and the CLI envelope's `egressPosture` carry. Raises the
launch's own `InvalidArgError` for options it would refuse. No AWS call and no credentials,
so a harness can decide before a build whether a no-network task is satisfiable.

Only `"sealed"` is network isolation, and no option answers it: isolation needs a VPC
egress connector and separately verified VPC routing without an internet gateway or NAT
gateway. Omitting `egress` is `"unsealed"`; `deny_egress` is `"best-effort"`. Without a
`region`, each connector ARN is checked against the region it names.

### `estimate_run`

```python
def estimate_run(
    size: SizeClass,
    *,
    running_seconds: SupportsFloat | SupportsIndex = 0.0,
    suspended_seconds: SupportsFloat | SupportsIndex = 0.0,
    image_gb: SupportsFloat | SupportsIndex | None = None,
    image_retained_seconds: SupportsFloat | SupportsIndex | None = None,
    suspend_resume_cycles: SupportsIndex = ...,
    snapshot_gb: SupportsFloat | SupportsIndex | None = None,
    launched: bool | None = None,
    label: str | None = None,
    rates: RateTable | None = None,
) -> CostReport
```

What a plan will cost, before spending anything (COST-10).

Takes plain seconds and marks every one of them projected. That is the difference from
\[`run_report`]: not the arithmetic, which is shared, but what the durations admit about
themselves — and it is why this signature has no `Duration` parameter at all, so an
accidentally-measured one is not something a caller can write.

Leave `launched` out and the core infers it: running time, or an image of non-zero size, so
a plan of suspended time alone reads no launch snapshot. Leave `label` out and the report
is labelled `"estimate"`, what `microvm cost --estimate` labels the same plan (#255). Leave
`suspend_resume_cycles` out and the plan counts none, as `microvm cost --estimate` does.

### `install_agent_access`

```python
def install_agent_access(
    session: Session,
    agents: SupportsGetItem[int, AgentSpec],
    token: BearerToken,
) -> None
```

Installs Bedrock access for `agents` into a running VM over `session`.

Three uploads (the environment file, Codex's config when Codex is among the agents,
the marker) and one root `chown` of `/workspace` to uid 1000. Re-runnable: a fresh
token overwrites the same files, which is how a twelve-hour token is refreshed.

### `install_agent_access_async`

```python
def install_agent_access_async(
    session: Session,
    agents: SupportsGetItem[int, AgentSpec],
    token: BearerToken,
) -> None
```

The awaitable twin of `install_agent_access`.

### `installed_agents`

```python
def installed_agents(session: Session) -> list[AgentSpec]
```

The agents a running VM was provisioned with, read from its guest marker.

For a process holding only a session (`Session.direct` from the identifier triple):
this is how a credential refresh learns which agents and models to re-provision. A VM
with no marker is refused as a precondition, naming `agent-up`.

### `installed_agents_async`

```python
def installed_agents_async(session: Session) -> list[AgentSpec]
```

The awaitable twin of `installed_agents`.

### `mint_bedrock_token`

```python
def mint_bedrock_token(
    region: Region,
    *,
    ttl_seconds: SupportsFloat | SupportsIndex | None = None,
) -> BearerToken
```

Mints a Bedrock bearer token from the default credential chain.

A SigV4 presign of `POST https://bedrock.amazonaws.com/?Action=CallWithBearerToken`,
base64, prefixed `bedrock-api-key-` — the reference generator's recipe, in process.
`ttl_seconds` defaults to the ceiling, twelve hours; more is refused by the core.

### `mint_bedrock_token_async`

```python
def mint_bedrock_token_async(
    region: Region,
    *,
    ttl_seconds: SupportsFloat | SupportsIndex | None = None,
) -> BearerToken
```

The awaitable twin of `mint_bedrock_token`.

### `mint_bedrock_token_with_credentials`

```python
def mint_bedrock_token_with_credentials(
    region: Region,
    *,
    access_key_id: str,
    secret_access_key: str,
    session_token: str | None = None,
    credentials_expires_at: SupportsFloat | SupportsIndex,
    ttl_seconds: SupportsFloat | SupportsIndex | None = None,
) -> BearerToken
```

Mint with explicit STS credentials and their expiry without changing process env.
`credentials_expires_at` is Unix seconds from STS Expiration. Secrets never enter repr.

### `preflight`

```python
def preflight(region: Region | None = None) -> PreflightReport
```

Whether a harness can launch in `region` (default: `$AWS_REGION`, `$AWS_DEFAULT_REGION`,
then us-east-1), checked before it queues work.

Three checks: the region resolves (advisory outside `Region.measured()`), the credential chain
resolves credentials (no AWS call), and one `ListManagedMicrovmImages` page answers in that
region, the only AWS operation, free and read-only. A check after a failure is reported
with `ran=False` and makes no call. Nothing billable, nothing mutating. It does not check
roles, the artifact bucket, quotas, or VPC connectors, and there is no boto3 service-model
check: this client speaks the API version it was built against, and the listing is the
evidence the endpoint accepts it. Never raises; read `report.ok`.

### `preflight_async`

```python
def preflight_async(region: Region | None = None) -> PreflightReport
```

The awaitable twin of `preflight`. Never raises either; read `report.ok`.

### `prompt_agent`

```python
def prompt_agent(
    session: Session,
    agent: AgentSpec,
    task: str,
    *,
    timeout_sec: SupportsFloat | SupportsIndex | None = None,
    exec_id: str | None = None,
    permission_mode: str = 'agent-default',
    reap_group_on_exit: bool = False,
) -> ExecHandle
```

Starts one task for `agent` over `session` and returns its handle. Does not wait.

Runs the agent's headless command as uid 1000 in `/workspace`, sourcing the installed
environment file. `timeout_sec` is the daemon-side budget for the agent process;
`exec_id` is the idempotency key for a retry that must not spawn twice.
`permission_mode` is agent-default or unrestricted. `reap_group_on_exit` stops
residual children after the main agent exits. Neither option grants guest root.

### `prompt_agent_async`

```python
def prompt_agent_async(
    session: Session,
    agent: AgentSpec,
    task: str,
    *,
    timeout_sec: SupportsFloat | SupportsIndex | None = None,
    exec_id: str | None = None,
    permission_mode: str = 'agent-default',
    reap_group_on_exit: bool = False,
) -> ExecHandle
```

The awaitable twin of `prompt_agent`.

### `provision_agentd`

```python
def provision_agentd(
    version: str | None = None,
    state_dir: str | PathLike[str] | None = None,
    binary: str | PathLike[str] | None = None,
) -> bytes
```

The `agentd` daemon binary for `version` (default: this client's own), as bytes.

Answered from `binary` or `$MICROVM_AGENTD` when either names a file, else the
version's cache entry under `state_dir` (default: the CLI's, so both share one cache),
else the GitHub release asset, verified in-process against the release workflow's
Sigstore attestation or, only when GitHub can't be reached for one, the release's
`SHA256SUMS`.
A fetch that cannot be verified raises `PreconditionError`, and so does any binary that
is not an aarch64 ELF. Neither `gh` nor `curl` is needed. Blocking: a fetch downloads a
few MiB and can take seconds.

### `provision_agentd_async`

```python
def provision_agentd_async(
    version: str | None = None,
    state_dir: str | PathLike[str] | None = None,
    binary: str | PathLike[str] | None = None,
) -> bytes
```

The awaitable twin of `provision_agentd`.

### `provision_agentd_report`

```python
def provision_agentd_report(
    version: str | None = None,
    state_dir: str | PathLike[str] | None = None,
    binary: str | PathLike[str] | None = None,
) -> ProvisionedAgentd
```

`provision_agentd`, answering with the bytes and how they got here: the source, the
verification, the path, the version, and the digest.

### `provision_agentd_report_async`

```python
def provision_agentd_report_async(
    version: str | None = None,
    state_dir: str | PathLike[str] | None = None,
    binary: str | PathLike[str] | None = None,
) -> ProvisionedAgentd
```

The awaitable twin of `provision_agentd_report`.

### `run_report`

```python
def run_report(
    size: SizeClass,
    *,
    running: Duration | None = None,
    suspended: Duration | None = None,
    image_build: Duration | None = None,
    image_gb: SupportsFloat | SupportsIndex | None = None,
    image_retained: Duration | None = None,
    suspend_resume_cycles: SupportsIndex = ...,
    snapshot_gb: SupportsFloat | SupportsIndex | None = None,
    launched: bool | None = None,
    label: str | None = None,
    rates: RateTable | None = None,
) -> CostReport
```

Per-phase attribution for one sandbox's lifecycle.

Every duration parameter takes a \[`PyDuration`] and not a number, which is what keeps
the provenance label attached: passing seconds here would need this function to pick a
provenance, and the one it would pick is the stronger claim.

`today` is the core's `CalendarDate::today_utc()` rather than a parameter. The core
takes it so a report is a pure function of its inputs and a test does not have to
travel in time; a Python caller who needs that reaches for the core through Rust, and
exposing a date here would be a knob whose only use is faking staleness.

Leave `launched` out and the core infers it: running time, or an image of non-zero size
(a launch reads a snapshot, so claiming one adds a transfer line). Leave `label` out and
the report is labelled `"run"`. Leave `suspend_resume_cycles` out and the report counts
none, so it has no suspend or resume line: a report claims only what the caller says
happened. All three defaults are the core's, the ones `microvm cost` applies.

### `session_constants`

```python
def session_constants() -> dict
```

The daemon's protocol constants, for a caller asserting against the wire contract.

### `wrap_dockerfile`

```python
def wrap_dockerfile(
    task_dockerfile: str,
    *,
    port: SupportsIndex | None = None,
    workdir: str | None = None,
    inherit_workdir: bool = False,
) -> str
```

A task Dockerfile with the agentd stanza appended, ready for `build_image`.

The result is the task text, a newline if it lacked one, `USER root` when the task's last
`USER` is anyone else, then the stanza the default Dockerfile uses: `COPY agentd /agentd`,
the chmod, `ENV AGENTD_PORT`, `EXPOSE`, `ENTRYPOINT []` and `CMD ["/agentd"]`. Pass the
result to `build_image` with `base_image=BaseImage.from_dockerfile(result)`.

`port` is the agent port (9000 by default) and must match the sandbox's. `workdir`
creates and sets a working directory, as the default Dockerfile does. `inherit_workdir`
refuses a result with no `WORKDIR` anywhere.

Raises `InvalidArgError` for a task with no `FROM`, one that ends inside a line
continuation or an unterminated heredoc (either would swallow the stanza), a keepalive
the client cannot tolerate, a port of 0, a workdir that is not one absolute path, or
`inherit_workdir` with nothing to inherit.

### `wrap_dockerfile_preserving_user`

```python
def wrap_dockerfile_preserving_user(
    task_dockerfile: str,
    *,
    port: SupportsIndex | None = None,
    workdir: str | None = None,
    inherit_workdir: bool = False,
) -> str
```

Preserve the final Dockerfile USER's uid and primary gid as the workload default.
The builder resolves stage inheritance, variables and names. Explicit image
AGENTD\_DEFAULT\_USER and request users win. The daemon starts as root and
workloads start only after readiness. Requires COPY `--chmod`.

## Constants

### `__version__`

```python
__version__: Final[str] = '1.3.0'
```

## Exceptions

Every exception derives from `MicrovmError`, so one `except` clause catches anything this package raises.

### `MicrovmError`

```python
class MicrovmError(Exception)
```

Base of every exception this library raises. Carries `.code`, `.kind`, `.wire_kind`,
and `.retryable` so nothing has to parse a message.

### `BuildWedgedError`

```python
class BuildWedgedError(MicrovmError)
```

ERR\_BUILD\_WEDGED — the image build was never scheduled: the clientToken replay
signature.

### `CredentialsError`

```python
class CredentialsError(MicrovmError)
```

ERR\_CREDENTIALS — an identity is wrong or absent; waiting will not fix it.

### `ExecFailedError`

```python
class ExecFailedError(MicrovmError)
```

ERR\_EXEC\_FAILED — the sandbox worked and the command in it exited non-zero. The one
failure that means nothing is wrong with the platform.

### `InterruptedError`

```python
class InterruptedError(MicrovmError)
```

ERR\_INTERRUPTED — interrupted after launch; teardown ran and any leak is named.

### `InvalidArgError`

```python
class InvalidArgError(MicrovmError)
```

ERR\_INVALID\_ARG — refused locally, before any AWS call. Every trap closure lands here.

### `LaunchDiedError`

```python
class LaunchDiedError(MicrovmError)
```

ERR\_LAUNCH\_DIED — the MicroVM reached a terminal state before RUNNING. Read the
message's stateReason.

### `PlatformError`

```python
class PlatformError(MicrovmError)
```

ERR\_PLATFORM — a control-plane failure with no more specific class.

### `PreconditionError`

```python
class PreconditionError(MicrovmError)
```

ERR\_PRECONDITION — a prerequisite is missing.

### `ProtocolError`

```python
class ProtocolError(MicrovmError)
```

ERR\_PROTOCOL — the daemon rejected the request on its merits. Read `.wire_kind` to
tell a 400 from a 404.

### `RetryableError`

```python
class RetryableError(MicrovmError)
```

ERR\_RETRYABLE — transient. Run the identical request again.

### `TimeoutError`

```python
class TimeoutError(MicrovmError)
```

ERR\_TIMEOUT — a client-side deadline elapsed. The VM and the exec are untouched.

### `UnexpectedError`

```python
class UnexpectedError(MicrovmError)
```

ERR\_UNEXPECTED — no handler claimed this. A bug in the client, not the platform.

### `WindowClosedError`

```python
class WindowClosedError(MicrovmError)
```

ERR\_WINDOW\_CLOSED — the MicroVM ended before the resume (the suspended window, the
maximum duration, or a terminate), and it can't be resumed; launch a new one. The
suspended window ends a VM the idle policy suspended; an explicit suspend stayed
resumable 1283 s into a 60 s window (measured 2026-10-12, us-east-1).

## Provenance

This page is generated from `bindings/microvms-py/microvms.pyi` by Griffe 2.3.0, run from `site/scripts/gen-reference.mjs` on every `pnpm run sync`, so an edit made here is overwritten by the next run.

To change the page, change the doc comment in the binding's Rust source, then regenerate the declarations with `mise run stubs`. `mise run stubs:check` fails when the committed file no longer matches the source.