# Errors and exceptions

Source: https://libtmux.org/en/tmux/topics/errors-and-exceptions/

> Handle command failures, inspect delivery status, and decide when a retry is safe.

A command can fail because tmux rejects it, or because the transport stops
before returning a reply. Inspect the reported error and delivery state before
retrying a mutation: tmux may already have received it.

For lookup failures caused by zero or multiple matches, see [Filtering and
queries](https://libtmux.org/en/tmux/concepts/queries/#the-cardinality-contract-side-by-side).

## A failed command, as a value

### Python
Command failures raise [`LibTmuxException`](<https://libtmux.org/en/py/latest/reference/libtmux-exc-libtmuxexception/>). It can carry a
`subcommand`; its string representation includes the subcommand and stderr.

### TypeScript
[`TmuxCommandError`](<https://libtmux.org/en/ts/latest/reference/errors-tmuxcommanderror/>) reports a command tmux rejected. [`TmuxTransportError`](<https://libtmux.org/en/ts/latest/reference/errors-tmuxtransporterror/>)
reports a failure to obtain a reply. Catch these error types separately
when the distinction affects recovery.

### Go
Operations return an error alongside their result. Inspect typed
errors and sentinel values with [`errors.As`](<https://pkg.go.dev/errors#As>) and [`errors.Is`](<https://libtmux.org/en/go/latest/reference/tmux-commanderror-is/>); preserve them
when wrapping with `%w`.

### Rust
Fallible operations return `Result<T, Error>`. Match the
non-exhaustive [`Error`](<https://libtmux.org/en/rs/latest/reference/error-error/>) enum and include a fallback for variants added later.

### Java
Command failures raise unchecked exceptions derived from
[`LibTmuxException`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-exception-libtmuxexception-libtmuxexception/>), which extends [`RuntimeException`](<https://docs.oracle.com/en/java/javase/21/docs/api/java/lang/RuntimeException.html>).

### C#
Failures raise subclasses of [`LibTmuxException`](<https://libtmux.org/en/csharp/latest/reference/libtmux-libtmuxexception/>), including
[`TmuxCommandException`](<https://libtmux.org/en/csharp/latest/reference/libtmux-tmuxcommandexception/>), [`TmuxTransportException`](<https://libtmux.org/en/csharp/latest/reference/libtmux-tmuxtransportexception/>), and
[`TmuxObjectNotFoundException`](<https://libtmux.org/en/csharp/latest/reference/libtmux-tmuxobjectnotfoundexception/>).

### C++
Fallible operations return `expected<T, CommandFailure>`.
[`CommandFailure`](<https://libtmux.org/en/cxx/latest/reference/libtmux-commandfailure/>) records the failure kind, delivery status, exit code, and
diagnostic.

### Swift
Fallible operations use typed throws with [`TmuxError`](<https://libtmux.org/en/swift/latest/reference/tmuxerror/>). Catch
that error to inspect the failure.

Use [`errors.Is`](<https://libtmux.org/en/go/latest/reference/tmux-commanderror-is/>) for sentinel errors such as [`tmux.ErrNoServer`](<https://libtmux.org/en/go/latest/reference/tmux-errnoserver/>) and
[`tmux.ErrNotFound`](<https://libtmux.org/en/go/latest/reference/tmux-errnotfound/>). Use [`errors.As`](<https://pkg.go.dev/errors#As>) to inspect a `*tmux.CommandError` and its
completed command result. Wrap errors with `%w` to preserve that information.

Distinguish a command rejected by tmux from a transport failure:

```typescript
import { TmuxCommandError, TmuxTransportError } from "libtmux";

try {
  await pane.capture();
} catch (error) {
  if (error instanceof TmuxCommandError) {
    error.args;     // the argument vector
    error.exitCode;
    error.stderr;    // tmux's own lines
  } else if (error instanceof TmuxTransportError) {
    error.kind;      // "cancelled" | "pipe" | "protocol" | "spawn" | "timeout"
    error.delivery;  // see below: this is the question a retry depends on
  }
}
```

## Is it safe to retry?

Retry a mutation automatically only when you know it was not dispatched, or when
repeating it is safe for your operation. A timeout, cancellation, or dropped
connection can occur after tmux has acted. These APIs expose delivery
information:

### TypeScript
[`TmuxTransportError.delivery`](<https://libtmux.org/en/ts/latest/reference/errors-tmuxtransporterror-delivery/>) distinguishes `not_started`,
[`written`](<https://libtmux.org/en/ts/latest/reference/selection-clientwhere-written/>), `replied`, and `indeterminate`. Only `not_started` establishes
that retrying cannot repeat an already-dispatched command.

### C#
[`LibTmuxException.Dispatch`](<https://libtmux.org/en/csharp/latest/reference/libtmux-libtmuxexception-dispatch/>) reports a [`TmuxDispatchState`](<https://libtmux.org/en/csharp/latest/reference/libtmux-tmuxdispatchstate/>):
[`NotDispatched`](<https://libtmux.org/en/csharp/latest/reference/libtmux-tmuxdispatchstate-notdispatched/>), [`Dispatched`](<https://libtmux.org/en/csharp/latest/reference/libtmux-tmuxdispatchstate-dispatched/>), or [`Unknown`](<https://libtmux.org/en/csharp/latest/reference/libtmux-tmuxdispatchstate-unknown/>). Treat the default, [`Unknown`](<https://libtmux.org/en/csharp/latest/reference/libtmux-tmuxdispatchstate-unknown/>),
as potentially dispatched.

### Java
`TmuxTimeoutException.outcome()` exposes a [`DispatchOutcome`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-transport-dispatchoutcome-dispatchoutcome/>):
[`NOT_DISPATCHED`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-transport-dispatchoutcome-dispatchoutcome-not_dispatched/>), `COMPLETE`, or `UNKNOWN`.

### C++
[`DeliveryStatus`](<https://libtmux.org/en/cxx/latest/reference/libtmux-deliverystatus/>) distinguishes [`not_started`](<https://libtmux.org/en/cxx/latest/reference/libtmux-deliverystatus-not_started/>), [`written`](<https://libtmux.org/en/cxx/latest/reference/libtmux-deliverystatus-written/>), [`replied`](<https://libtmux.org/en/cxx/latest/reference/libtmux-deliverystatus-replied/>),
and [`indeterminate`](<https://libtmux.org/en/cxx/latest/reference/libtmux-deliverystatus-indeterminate/>).

### Rust
With the `control-mode` feature, [`ControlModeErrorKind.DispatchTimedOut`](<https://libtmux.org/en/rs/latest/reference/error-controlmodeerrorkind-dispatchtimedout/>)
means the command was not dispatched. A plain timeout can occur after tmux
received the command, so do not infer that retrying is safe.

### Python
Inspect the exit status after a subprocess completes. If the call was
interrupted, verify the resulting tmux state before repeating a mutation.

### Go
Inspect the exit status after a subprocess completes. If the call was
interrupted, verify the resulting tmux state before repeating a mutation.
A failed pooled control connection is retired, but its failure does not
prove that a mutation was never dispatched.

### Swift
A control-session failure does not establish that tmux never received
the command. Retry only when non-delivery is known or repeating the
operation is safe.

```typescript
import { TmuxTransportError } from "libtmux";

try {
  await session.newWindow({ name: "build" });
} catch (error) {
  if (error instanceof TmuxTransportError && error.delivery === "not_started") {
    // safe to retry: nothing reached tmux
  }
}
```

```csharp
try
{
    await session.CreateWindowAsync(new NewWindowRequest(name: "build"));
}
catch (LibTmuxException error) when (error.Dispatch == TmuxDispatchState.NotDispatched)
{
    // safe to retry
}
```

```cpp
auto result = session.new_window({.name = "build"});
if (!result.has_value() && result.error().delivery == libtmux::DeliveryStatus::not_started) {
  // safe to retry
}
```

Treat unknown delivery as potentially executed.

A [`not_started`](<https://libtmux.org/en/cxx/latest/reference/libtmux-deliverystatus-not_started/>) result means the request did not reach tmux. A [`written`](<https://libtmux.org/en/cxx/latest/reference/libtmux-deliverystatus-written/>) result
means the transport accepted the request but no terminal reply arrived.
[`NotDispatched`](<https://libtmux.org/en/csharp/latest/reference/libtmux-tmuxdispatchstate-notdispatched/>) identifies a request that did not reach tmux.
[`NOT_DISPATCHED`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-transport-dispatchoutcome-dispatchoutcome-not_dispatched/>) identifies a request that did not reach tmux.
[`DispatchTimedOut`](<https://libtmux.org/en/rs/latest/reference/error-controlmodeerrorkind-dispatchtimedout/>) identifies a request that did not reach tmux.

For subprocess calls that complete normally, inspect the exit status. If a call
is interrupted or times out without a delivery state, check tmux's resulting
state before repeating a mutation.
