Errors and cancellation types#

tractor extends trio’s “exceptions always propagate” rule across the process boundary: a crash in any actor is serialized as an Error msg, shuttled over IPC, and re-raised in the linked parent scope as a boxed RemoteActorError — preserving the original type, traceback text and source-actor identity, even across multi-hop relays (a.k.a. “inceptions”).

The most-used types below are importable from tractor directly; the remainder live in tractor._exceptions (not yet re-exported at top level).

Boxed remote errors#

exception tractor.RemoteActorError(message, ipc_msg=None, boxed_type=None, **extra_msgdata)[source]#

A box(ing) type which bundles a remote actor BaseException for (near identical, and only if possible,) local object/instance re-construction in the local process memory domain.

Normally each instance is expected to be constructed from a special “error” IPC msg sent by some remote actor-runtime.

Parameters:
Return type:

None

property boxed_type: Type[BaseException] | None#

Error type boxed by last actor IPC hop.

pformat(with_type_header=True)[source]#

Format any boxed remote error by multi-line display of,

  • error’s src or relay actor meta-data,

  • remote runtime env’s traceback,

With optional control over the format of,

  • whether the boxed traceback is ascii-decorated with a surrounding “box” annotating the embedded stack-trace.

  • if the error’s type name should be added as margins around the field and tb content like:

    <RemoteActorError(.. <<multi-line-content>> .. )>

  • the placement of the .message: str (explicit equiv of .args[0]), either placed below the .tb_str or in the first line’s header when the error is raised locally (since the type name is already implicitly shown by python).

Parameters:

with_type_header (bool)

Return type:

str

try:
    async with portal.open_context(ep_fn) as (ctx, first):
        ...
except tractor.RemoteActorError as rae:
    if rae.boxed_type is ValueError:
        ...  # remote task raised a `ValueError`
exception tractor.ContextCancelled(message, ipc_msg=None, boxed_type=None, **extra_msgdata)[source]#

Bases: RemoteActorError

Inter-actor task context was cancelled by either a call to Portal.cancel_actor() or Context.cancel().

Parameters:
Return type:

None

property canceller: tuple[str, str] | None#

Return the (maybe) Actor.uid for the requesting-author of this ctxc.

Emit a warning msg when .canceller has not been set, which usually idicates that a None msg-loop setinel was sent before expected in the runtime. This can happen in a few situations:

  • (simulating) an IPC transport network outage

  • a (malicious) pkt sent specifically to cancel an actor’s runtime non-gracefully without ensuring ongoing RPC tasks are incrementally cancelled as is done with:

    `Actor`
    |_`.cancel()`
    |_`.cancel_soon()`
    |_`._cancel_task()`
    

Note

Inspect ContextCancelled.canceller (the requesting actor’s uid) to distinguish a self-requested cancel (absorbed at open_context() exit) from a cross-actor cancel (raised locally) — the full rules live in Contexts and streaming.

Typed-messaging errors#

exception tractor.MsgTypeError(message, ipc_msg=None, boxed_type=None, **extra_msgdata)[source]#

Bases: RemoteActorError

Equivalent of a runtime TypeError for IPC dialogs.

Raise when any IPC wire-message is decoded to have invalid field values (due to type) or for other MsgCodec related violations such as having no extension-type for a field with a custom type but no enc/dec_hook() support.

Can be raised on the send or recv side of an IPC Channel depending on the particular msg.

Msgs which cause this to be raised on the .send() side (aka in the “ctl” dialog phase) include: - Start - Started - Return

Those which cause it on on the .recv() side (aka the “nasty streaming” dialog phase) are: - Yield - TODO: any embedded .pld type defined by user code?

Normally the source of an error is re-raised from some .msg._codec decode which itself raises in a backend interchange lib (eg. a msgspec.ValidationError).

Parameters:
Return type:

None

property bad_msg: PayloadMsg | None#

Ref to the the original invalid IPC shuttle msg which failed to decode thus providing for the reason for this error.

An “IPC TypeError”: a message failed validation against the active msg-spec / pld_spec (see Typed messaging: tractor.msg). Raised sender-side for control msgs (Started/Return) and receiver-side for stream Yield payloads.

exception tractor._exceptions.StreamOverrun(message, ipc_msg=None, boxed_type=None, **extra_msgdata)[source]#

Bases: RemoteActorError, TooSlowError

Parameters:
Return type:

None

The sender out-paced the receiver’s buffer on a MsgStream opened without allow_overruns=True; subtypes trio.TooSlowError.

Transport and runtime errors#

exception tractor.TransportClosed(message, loglevel='transport', src_exc=None, raise_on_report=False)[source]#

IPC transport (protocol) connection was closed or broke and indicates that the wrapping communication Channel can no longer be used to send/receive msgs from the remote peer.

Parameters:
Return type:

None

exception tractor.ModuleNotExposed[source]#

Bases: ModuleNotFoundError

The requested module is not exposed for RPC

Raised when an RPC requests a function from a module not listed in the target actor’s enable_modules allowlist — capability-style access control, not an import bug on your end ;)

exception tractor._exceptions.NoRuntime[source]#

Bases: RuntimeError

The root actor has not been initialized yet

Raised by tractor.current_actor() (and friends) when no actor runtime is up in the current process.

exception tractor._exceptions.ActorTooSlowError[source]#

Bases: RuntimeFailure

A peer-Actor failed to ack an actor-runtime cancel-cascade request (e.g. Portal.cancel_actor() -> Actor.cancel()) within the bounded wait window.

Distinct exc-type (NOT a trio.TooSlowError subclass) so that except trio.TooSlowError: blocks elsewhere in the test-suite or tractor internals do NOT silently mask actor-cancel timeouts — these MUST propagate so a supervisor can escalate to proc.terminate() (hard-kill) per SC-discipline:

graceful cancel-req -> bounded wait -> hard-kill

Reason: see #subint_forkserver duplicate-name hang diagnosis where Portal.cancel_actor() silently swallowed the timeout and the supervisor never escalated, leaving a same-named sibling subactor parked forever.

A peer actor failed to ack a cancel request within the bounded wait — the SC-sanctioned escalation signal from APIs like Portal.cancel_actor(raise_on_timeout=True). Catch it to escalate (e.g. hard-kill via the supervising ActorNursery); never just ignore it, that’s how zombies happen.

See also

Contexts and streaming for how cancellation and errors flow through a Context, Debugging and devx: tractor.devx for crash-handling REPL tooling (debug_mode, post-mortems), and Cancellation and error propagation for the full SC-cancellation story.