How Croc Handles Transfer Interruptions and Reconnections
Croc detects broken TCP connections using transferDisconnectError, then automatically retries up to 10 times with exponential backoff while negotiating a new relay room and requesting only missing file chunks to resume interrupted transfers.
Croc is engineered to survive flaky networks and sudden drops. When a connection fails during a file transfer, the schollz/croc repository does not simply terminate; instead, it executes a sophisticated reconnection protocol that preserves progress and minimizes retransmission. This resilience is implemented through error wrapping, backoff strategies, and delta synchronization directly in the Go source code.
Detecting Disconnections with transferDisconnectError
When the underlying TCP link drops, croc immediately wraps the error in a transferDisconnectError structure. In src/croc/croc.go at lines 302‑306, the library defines this wrapper to distinguish between fatal errors and transient network failures. The function isTransferDisconnectError at lines 93‑96 then inspects the error chain to determine if a retry is appropriate, triggering the reconnection routine only when the disconnect is identified as recoverable.
Reconnection Strategy: Backoff and Room Generation
Once a disconnect is detected, croc enters a controlled retry loop governed by three key mechanisms defined in src/croc/croc.go:
maxReconnectAttempts(lines 55‑56): Caps retries at 10 attempts by default to prevent infinite loops.reconnectBackoff(lines 79‑90): Implements exponential backoff with a maximum delay of 5 seconds between attempts.generateReconnectRoom(lines 71‑77): Creates a fresh random room name for the resumed session, ensuring the new connection bypasses any stale relay state.
This combination prevents thundering herds while giving the network time to recover.
Version Negotiation Protocol
To ensure both endpoints speak the same resume language, croc exchanges versioning tokens during reconnection. The client maintains a reconnectVersion constant (lines 52‑54) and compares it against the peerReconnectVersion reported by the remote side. If the peer reports a higher version, the adopting client upgrades its protocol accordingly, guaranteeing backward compatibility during intermittent reconnections.
Resuming Transfers with RemoteFileRequest
After establishing a new relay room and agreeing on protocol versions, the receiver sends a RemoteFileRequest (lines 104‑110) that lists specific chunk ranges still needed to complete the file. The sender then invokes finishSenderData (lines 51‑69) to transmit only those missing pieces rather than restarting from byte zero. This delta synchronization dramatically reduces bandwidth usage on large files.
TCP Timeout Adjustments for Resume Payloads
Standard TCP timeouts are too aggressive for resume operations that must transmit large control messages. In src/comm/comm.go at line 30, croc raises the read deadline to 10 minutes via messageBodyReadTimeout specifically for resume-control messages. This ensures that even massive "missing-chunk" lists can traverse slow or high-latency relays without triggering premature timeouts.
CLI Flags for Automatic Resume Behavior
User interaction during resume is controlled through flags defined in src/cli/cli.go. At line 125, the --overwrite flag automatically attempts to resume an interrupted transfer without prompting. When combined with --no-prompt, croc becomes fully autonomous, silently recovering from network interruptions using the stored session state.
Practical Usage Examples
Resume a transfer automatically using the default behavior:
croc send --code abcdef file.txt # Start transfer
# … network drops …
croc receive --code abcdef # Re-run the same command
# croc detects the previous session, creates a new relay room,
# asks the sender for missing chunks and continues.
Force resume without user interaction:
croc receive --code abcdef --overwrite # Skip the "overwrite?" prompt
Programmatically check for disconnects in Go:
if err != nil {
if croc.IsTransferDisconnectError(err) {
// retry logic is embedded in croc.Client.Start()
// simply call Start() again or let croc handle the reconnection.
}
}
Summary
- Error Detection: Uses
transferDisconnectErrorinsrc/croc/croc.golines 302‑306 to identify recoverable network failures. - Retry Limits: Enforces a maximum of 10 reconnection attempts with exponential backoff capped at 5 seconds.
- Fresh Sessions: Generates new relay rooms via
generateReconnectRoomto avoid stale connection state. - Protocol Safety: Exchanges
reconnectVersiontokens to ensure compatible resume semantics between peers. - Delta Sync: Requests missing chunks through
RemoteFileRequestinstead of retransmitting entire files. - Timeout Tolerance: Extends TCP read deadlines to 10 minutes in
src/comm/comm.gofor large resume payloads.
Frequently Asked Questions
What happens if croc exceeds the maximum reconnection attempts?
If the network remains down after 10 attempts, croc aborts the transfer and returns the last encountered error. The exponential backoff (max 5 seconds) ensures the process does not hammer the relay, but once the cap is reached, the user must manually restart the transfer.
How does croc prevent version mismatches during a resumed transfer?
The client carries a reconnectVersion token defined at lines 52‑54 of src/croc/croc.go. During reconnection, both sides exchange this value; if peerReconnectVersion is higher, the client adopts the peer's protocol level. This handshake guarantees that sender and receiver agree on how to interpret the RemoteFileRequest and chunk sequencing.
Why does croc create a new relay room instead of reusing the old one?
The generateReconnectRoom function (lines 71‑77) creates a new random room name to bypass potential stale state in the relay or NAT tables. TCP connections often leave ghost sessions in intermediary proxies; a fresh room ensures both endpoints negotiate a clean slate while maintaining continuity through the versioning and chunk-tracking logic.
Can I force croc to resume without prompting the user?
Yes. Supply the --overwrite flag (line 125 in src/cli/cli.go) when running croc receive. This flag tells croc to automatically attempt resuming interrupted transfers. Combine it with --no-prompt for fully automated recovery in scripts or CI/CD pipelines.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →