> ## Documentation Index
> Fetch the complete documentation index at: https://hayate.shiina.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Handling EngineError Variants in the Hayate Rust Library

> EngineError is the top-level error returned by all Hayate async functions. Match variants, cancel transfers, and recover from failures.

Every fallible function in the `hayate` crate returns `Result<T, EngineError>`. `EngineError` is a single flat enum that covers the full range of failure modes across the engine — from file system I/O errors and QUIC transport failures to cryptographic handshake problems and protocol mismatches. Because all errors are unified under one type, you can handle them with a single `match` arm in most callers, and drill into specifics only where your application needs to recover or provide user-facing messages.

## EngineError Variants

| Variant                              | Description                                                         |
| ------------------------------------ | ------------------------------------------------------------------- |
| `Io(io::Error)`                      | File system or socket I/O error                                     |
| `TimedOut(String)`                   | Operation timed out waiting for a peer or data                      |
| `Cancelled(String)`                  | Operation cancelled by the caller via the progress callback         |
| `ProtocolMismatch { local, remote }` | Protocol version mismatch between sender and receiver               |
| `TransferRejected`                   | Receiver explicitly rejected the transfer via the consent callback  |
| `InvalidPassphrase`                  | Key exchange authentication failed due to a wrong pairing code      |
| `Crypto(&str)`                       | Encryption, decryption, or HKDF key derivation error                |
| `InvalidFrame(String)`               | Malformed or oversized frame encountered on the stream              |
| `Handshake(String)`                  | Handshake negotiation failed (e.g., endpoint closed during pairing) |
| `Connect(ConnectError)`              | QUIC connection initiation error                                    |
| `Connection(ConnectionError)`        | Error on an active QUIC connection                                  |
| `OpenStream(OpenStreamError)`        | Error opening a bidirectional stream on the connection              |
| `Write(WriteError)`                  | Error writing data to the QUIC stream                               |
| `ClosedStream(ClosedStream)`         | Write attempted on an already-closed stream                         |
| `PathTraversal`                      | Archive entry attempted path traversal outside the output directory |
| `Compression(String)`                | Zstd compression or decompression error                             |

## Matching Errors

Match on specific variants to give users actionable feedback or apply targeted recovery logic:

```rust theme={null}
use hayate::{HayateSender, EngineError};

match sender.send("./file.txt", |_| Ok(())).await {
    Ok(checksum) => println!("Done: {checksum}"),
    Err(EngineError::InvalidPassphrase) => {
        eprintln!("Wrong pairing code — make sure sender and receiver use the same phrase");
    }
    Err(EngineError::TransferRejected) => {
        eprintln!("The receiver declined the transfer");
    }
    Err(EngineError::TimedOut(msg)) => {
        eprintln!("Transfer timed out: {msg}");
    }
    Err(EngineError::Io(e)) => {
        eprintln!("I/O error: {e}");
    }
    Err(e) => eprintln!("Transfer failed: {e}"),
}
```

## Cancelling a Transfer

You can abort an in-progress transfer at any time by returning `Err(EngineError::Cancelled(...))` from the progress callback. The engine stops sending immediately and propagates the error as the return value of `.send()` or `.receive()`:

```rust theme={null}
use hayate::{HayateSender, EngineError};

let mut bytes_so_far = 0u64;

let checksum = sender.send("./file.txt", |bytes| {
    bytes_so_far = bytes;
    if bytes_so_far > 500_000_000 {
        return Err(EngineError::Cancelled("file too large".into()));
    }
    Ok(())
}).await?;
```

This is the recommended way to implement user-initiated cancel buttons, size guards, or timeout policies at the application layer.

<Note>
  `EngineError` is marked `#[non_exhaustive]`. New variants may be added in future minor releases without a breaking change. Always include a wildcard arm (`Err(e) => ...`) at the end of your `match` to ensure your code compiles against newer versions of the crate.
</Note>
