SoulFire LogoSoulFire
SDK

Errors, retries, and cancellation

Handle SoulFire errors without losing information that helps you find the cause.

SoulFire separates transport errors, rejected game actions, and failed tasks. The error type tells the application whether to retry, change input, inspect progress, or request permission.

Error families

FailureMeaningTypical response
Connection or compatibilityHandshake, API version, or required capability failedStop startup and report the incompatibility
RPCThe remote call failed before producing a semantic resultInspect code and retryable
ActionThe server processed an atomic action but did not complete itInspect the action result and correct state or input
TaskA durable task reached a failed or cancelled terminal stateInspect failure code, progress, and retryability
PluginDiscovery, descriptor, compatibility, or plugin invocation failedCheck installation, version, and grants
Local lifecycleA session, lease, container, or client is already closedReopen the scoped resource

Effect error channels

The Effect entry point exposes tagged errors. Handle expected cases without catching defects:

const program = bot.chat.send("hello").pipe(
  Effect.catchTag("SoulFireRpcError", (error) =>
    error.retryable
      ? Effect.retry(
          bot.chat.send("hello"),
          Schedule.exponential("100 millis").pipe(
            Schedule.compose(Schedule.recurs(4)),
          ),
        )
      : Effect.fail(error)
  ),
);

SoulFireRpcError preserves the operation name, ConnectRPC code, request ID, retryability, and original cause. Connection, task, plugin, and behavior operations use their own tagged error types.

Effect interruption cancels the underlying request or stream. Acquire clients, sessions, leases, and local server processes in a scope so interruption also runs their finalizers.

Promise and Python errors

The Promise facade rejects with the typed error value from the Effect channel. It does not leak an Effect FiberFailure.

try {
  await bot.chat.send("hello");
} catch (error) {
  if (error instanceof SoulFireRpcError && error.retryable) {
    console.warn(error.code, error.requestId, error.cause);
  } else {
    throw error;
  }
}

Python RPC failures raise SoulFireRpcError with matching diagnostic fields:

try:
    await bot.chat.send("hello")
except SoulFireRpcError as error:
    if not error.retryable:
        raise
    print(error.code, error.request_id, error.cause)

Pass AbortSignal and ConnectRPC call options in TypeScript. Pass timeout_ms in Python. Python async chat waits use the standard asyncio.timeout cancellation model.

Retry safely

Retry only when both conditions hold:

  • The error reports retryable.
  • Repeating the operation cannot duplicate a game-side mutation.

Use an idempotency key for supported mutations and task starts. SoulFire stores the completed response for the key within its bounded retention window. A retry with the same key receives the original result instead of repeating the action.

Do not retry invalid arguments, denied permissions, stale entity epochs, closed containers, unreachable path goals, or deterministic plugin validation errors without changing the request.

A transport timeout does not prove that the server did nothing. Use an idempotency key or query the durable task handle before submitting the same mutation again.

Include the request ID, operation, server version, SDK version, bot ID, and task ID in production logs. Do not log access tokens, plugin secrets, chat credentials, or raw packet payloads that can contain private data.

How is this page?

Last updated on

On this page