How Croc Supports Reconnection and Resuming Interrupted Transfers

Croc automatically detects network disconnections, implements exponential backoff with a 10-attempt limit, negotiates resume protocol versions with peers, and transfers only missing chunks to seamlessly resume interrupted file transfers.

Croc is an open-source command-line file transfer tool designed to survive flaky networks where traditional tools would fail completely. When TCP connections drop mid-transfer, croc does not terminate the process; instead, it enters a sophisticated reconnection routine that negotiates fresh relay rooms and resumes exactly where the transfer left off. This article examines the specific mechanisms in src/croc/croc.go, src/comm/comm.go, and supporting modules that make interruption-tolerant transfers possible.

Detecting Transfer Interruptions

Identifying Disconnects with transferDisconnectError

When a TCP link drops, croc wraps the underlying error in a custom transferDisconnectError type defined in src/croc/croc.go lines 302-306. The helper function isTransferDisconnectError (lines 93-96) recognizes this wrapper and distinguishes between fatal errors (like corrupted files) and recoverable network drops (like WiFi hiccups or mobile handoffs). Upon detection, croc triggers the reconnection routine instead of exiting.

Reconnection Strategy and Backoff Mechanisms

Limiting Retry Attempts

The reconnection logic respects a hard ceiling defined by maxReconnectAttempts in src/croc/croc.go lines 55-56, which defaults to 10 attempts. This prevents infinite retry loops against permanently offline peers while accommodating temporary network fluctuations.

Exponential Backoff Timing

Between attempts, croc implements reconnectBackoff (lines 79-90) to calculate delays using exponential backoff capping at 5 seconds. This reduces relay server load and allows transient network issues to resolve naturally.

Generating Fresh Relay Rooms

To resume a transfer, croc must establish a new communication channel independent of the broken connection. The generateReconnectRoom function (lines 71-77) creates a cryptographically random room name for the resumed session, ensuring the client and sender can reconnect through the relay without interfering with other active transfers or the stale session.

Negotiating Resume Protocol Versions

Before exchanging file data, croc ensures both peers speak the same resume protocol. The client maintains a reconnectVersion (lines 52-54 in src/croc/croc.go) exchanged during the handshake. If the peer reports a higher version (peerReconnectVersion), the client immediately adopts it, guaranteeing backward and forward compatibility during the resume process.

Resuming Interrupted Transfers with Chunk Ranges

Requesting Missing Chunks via RemoteFileRequest

Once the reconnection handshake completes, the receiver transmits a RemoteFileRequest (lines 104-110) containing the specific byte ranges or chunks still needed to complete the file. The sender responds by transmitting only those missing pieces rather than restarting the entire transfer from byte zero.

Handling Large Resume Payloads

For transfers involving multi-gigabyte files, the list of missing chunks can become substantial. To prevent timeouts during this control phase, src/comm/comm.go line 30 raises the messageBodyReadTimeout to 10 minutes, ensuring that extensive chunk lists can traverse slow relays without dropping the connection.

Completing the Transfer

The sender finalizes the resume by executing finishSender (lines 51-69 in src/croc/croc.go), writing only the requested missing chunks to the wire while the receiver writes them to the appropriate offsets in the target file. The underlying TCP wrapper in src/comm/comm.go and connection logic in src/tcp/tcp.go handle the actual byte streaming during this phase.

CLI Options for Automatic Resume

Using the Overwrite Flag

According to src/cli/cli.go line 125, users can invoke the --overwrite flag to automatically attempt resuming interrupted transfers without interactive prompting. Without this flag, croc asks for user confirmation unless --no-prompt is also set.

Resume a transfer automatically with default behavior:

croc send --code abcdef large-file.zip        # Start transfer

# ... network drops ...

croc receive --code abcdef                     # Re-run the same command

# croc detects the previous session, creates a new relay room,

# asks for missing chunks, and continues.

Force resume without prompting:

croc receive --code abcdef --overwrite         # Skip the "overwrite?" prompt

Programmatic handling of disconnections:

if err != nil {
    if croc.IsTransferDisconnectError(err) {
        // Reconnection logic is embedded in croc.Client.Start()
        // Simply allow Start() to continue or call it again.
    }
}

Summary

  • Error Detection: Croc wraps TCP errors in transferDisconnectError (src/croc/croc.go lines 302-306) to identify recoverable network drops.
  • Retry Limits: The client attempts reconnection up to 10 times with exponential backoff (max 5 seconds) via maxReconnectAttempts and reconnectBackoff.
  • Fresh Channels: generateReconnectRoom creates new random room names to avoid session conflicts.
  • Version Negotiation: reconnectVersion ensures both peers agree on the resume protocol before exchanging data.
  • Partial Transfer: RemoteFileRequest requests only missing chunks, while messageBodyReadTimeout (10 minutes in src/comm/comm.go line 30) accommodates large chunk lists.
  • CLI Control: The --overwrite flag in src/cli/cli.go line 125 enables automatic resume without user intervention.

Frequently Asked Questions

How many times will croc attempt to reconnect before giving up?

Croc defaults to 10 reconnection attempts as defined by maxReconnectAttempts in src/croc/croc.go lines 55-56. After exhausting these attempts, the transfer terminates and returns an error.

Does croc restart the entire file when resuming a transfer?

No. Croc uses RemoteFileRequest (lines 104-110) to communicate exactly which chunks are missing. The sender then transmits only those pieces via finishSender (lines 51-69), appending them to the partially received file without restarting from byte zero.

What happens if the sender and receiver have different versions of croc?

The resume protocol includes version negotiation through reconnectVersion and peerReconnectVersion (lines 52-54). Both sides automatically adopt the higher version number, ensuring backward compatibility and preventing protocol mismatches during reconnection.

How long will croc wait for a large list of missing chunks to transfer?

Croc sets messageBodyReadTimeout to 10 minutes in src/comm/comm.go line 30 specifically for resume control messages. This extended timeout accommodates huge file manifests that might otherwise timeout on slow relays, while normal data chunks use shorter deadlines.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →