# Croc Reconnect Mechanism: How It Resumes Interrupted File Transfers Automatically

> Croc automatically resumes interrupted file transfers with its reconnect mechanism. It uses exponential back-off delays and retries up to 10 times without manual intervention.

- Repository: [Zack/croc](https://github.com/schollz/croc)
- Tags: internals
- Published: 2026-07-30

---

**Croc automatically resumes interrupted transfers by creating a new reconnect room, applying exponential back-off delays, and cycling through remembered relay addresses to re-establish the PAKE handshake, supporting up to 10 retry attempts without requiring manual intervention.**

The `schollz/croc` file transfer tool implements a robust **croc reconnect mechanism** that allows large file transfers to survive network interruptions without restarting from scratch. Located primarily in [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go), this protocol handles disconnection detection, state preservation, and secure re-handshaking automatically when connections drop.

## Core Concepts of the Croc Reconnect Protocol

### Version Negotiation and Compatibility

Before attempting any reconnection, croc validates that both sender and receiver support the reconnect protocol. The system defines a **ReconnectVersion** constant (set to `1` in [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go)). If either peer reports an older version, the reconnect mechanism disables itself via `canRetryTransfer` to prevent protocol mismatches.

### State Reset and Exponential Back-Off

When a `transferDisconnectError` occurs, croc initiates a controlled reset via `resetForReconnectAttempt`. This function clears file pointers, resets cryptographic key state, creates a fresh `nextReconnectRoom` for the retry attempt, and zeroes internal chunk counters.

The system then applies an exponential back-off strategy through `reconnectBackoff(attempt)`, capping the delay at 5 seconds between retries. The default `maxReconnectAttempts` is set to **10**.

### Relay Candidate Selection

Croc maintains a memory of successful connections in `reconnectRelayAddresses`. During a retry, `reconnectRelayCandidates` builds an ordered list prioritizing the current control address first, followed by any previously remembered relay addresses. This ensures croc attempts the most likely working endpoints before falling back to alternatives.

### PAKE Handshake Re-execution

Reconnection requires re-authentication. The sender invokes `senderReconnectRelayAttempt`, which internally calls `senderWaitForHandshake` to re-run the PAKE (Password Authenticated Key Exchange) protocol. The receiver uses `receiverReconnectRelayAttempt`, sending a single `handshakeRequest` byte to trigger the exchange. Once authenticated, `transferWithReconnect` loops back into the standard transfer routine.

## Step-by-Step Reconnection Flow

When a network interruption occurs, croc executes the following sequence in [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go):

1. **Disconnection Detection**: The core `transfer()` function returns a `transferDisconnectError` when the underlying TCP/relay connection drops.

2. **Retry Validation**: `canRetryTransfer` checks three conditions: the reconnect version compatibility, existence of `nextReconnectRoom`, and whether the attempt counter remains below `maxReconnectAttempts`.

3. **Back-off Application**: The system sleeps for the duration calculated by `reconnectBackoff(attempt)`.

4. **State Reset**: `resetForReconnectAttempt` clears per-file metadata while preserving the transfer progress (file offsets and chunk maps) stored in the client instance.

5. **Relay Dialing**: `reconnectRelayCandidates` assembles the candidate list, and `reconnectRelayAttempt` dials each address sequentially until one accepts the connection.

6. **Secure Re-handshake**: For senders, `senderReconnectRelayAttempt` runs the full PAKE handshake. Receivers trigger the handshake via `receiverReconnectRelayAttempt`.

7. **Transfer Resumption**: Upon successful handshake, `transferWithReconnect` resumes the `transfer()` loop, continuing from the last acknowledged byte.

## Practical Usage and Code Examples

### Automatic Recovery via CLI

The reconnect mechanism requires no special flags. If a transfer interrupts, simply rerun the original command:

```bash

# Terminal 1 - Sender

croc send large-file.iso

# Terminal 2 - Receiver  

croc receive

```

Croc detects the previous session state, creates the reconnect room automatically, and resumes the transfer.

### Debug Output

Enable `--debug` to observe the reconnect logic:

```bash
croc --debug send file.txt

```

Example output during reconnection:

```text
debug: reconnect attempt 1 after 100ms
debug: resetting transfer state for reconnect attempt 1
debug: could not reconnect to any relay: 127.0.0.1:9009: connection refused

```

### Go API Implementation

Developers using the Go library get automatic reconnect for free:

```go
c, _ := croc.New(croc.Options{
    SharedSecret: "secret-code",
    IsSender:     true,
})

// Transfer automatically handles disconnections internally
err := c.Send(filesInfo, emptyFolders, totalFolders)
if err != nil {
    log.Fatal("Transfer failed after retries:", err)
}

```

The `Client` struct internally invokes `transferWithReconnect`, wrapping all retry logic transparently.

## Key Source Files Implementing Reconnect

- **[`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go)**: Contains `ReconnectVersion`, `maxReconnectAttempts`, `transferWithReconnect`, `resetForReconnectAttempt`, and `reconnectBackoff`.
- **[`src/croc/ctx.go`](https://github.com/schollz/croc/blob/main/src/croc/ctx.go)**: Provides cancellable context (`c.stop`) for clean aborts during retries.
- **[`src/tcp/tcp.go`](https://github.com/schollz/croc/blob/main/src/tcp/tcp.go)**: Low-level TCP wrapper (`ConnectToTCPServer`) used by reconnection attempts.
- **[`src/comm/comm.go`](https://github.com/schollz/croc/blob/main/src/comm/comm.go)**: Connection abstraction allowing the reconnect routine to swap underlying `Comm` objects while preserving client state.
- **[`src/message/message.go`](https://github.com/schollz/croc/blob/main/src/message/message.go)**: Defines PAKE handshake messages re-exchanged during `senderReconnectRelayAttempt` and `receiverReconnectRelayAttempt`.

## Summary

- **Croc's reconnect mechanism** operates entirely within [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go), requiring no manual user intervention.
- The system supports **up to 10 retry attempts** with exponential back-off (max 5-second delay).
- **State preservation** ensures file transfers resume from the exact byte offset where interruption occurred.
- **Relay candidate caching** (`reconnectRelayAddresses`) prioritizes known-good endpoints during reconnection sweeps.
- **Version checking** via `ReconnectVersion` prevents incompatible peers from attempting reconnection.

## Frequently Asked Questions

### How many times will croc retry a failed connection?

By default, croc attempts to reconnect **10 times** before giving up. This limit is defined by the `maxReconnectAttempts` constant in [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go). Each retry applies an exponential back-off delay calculated by `reconnectBackoff`, capped at 5 seconds between attempts.

### Do I need special flags to enable reconnect?

No. The **croc reconnect mechanism** is automatic and transparent. Simply rerun the same `croc send` or `croc receive` command after a failure. The tool detects the previous session through the shared secret, creates a `nextReconnectRoom`, and attempts to resume without additional configuration.

### What happens if the protocol version mismatches during reconnect?

If `canRetryTransfer` detects that either peer reports a `ReconnectVersion` older than the current protocol version (version `1`), reconnection is immediately disabled. This prevents state corruption and forces a fresh transfer rather than attempting incompatible reconnection logic.

### How does croc know where to resume the file transfer?

Before executing `resetForReconnectAttempt`, croc preserves the transfer progress in the `Client` struct, including file offsets and chunk acknowledgment maps. After successfully re-handshaking via `senderReconnectRelayAttempt` or `receiverReconnectRelayAttempt`, the `transfer` function resumes reading from these preserved offsets, ensuring no data duplication or loss.