How to Troubleshoot Common Issues with croc: Complete Diagnostics Guide
To troubleshoot common issues with croc, verify relay ports are free with nc -zv, ensure the CROC_SECRET environment variable is exported before running commands, bypass overwrite prompts using --yes, and enable --debug for detailed connection logs.
croc is a Go-based peer-to-peer file transfer tool that encrypts data in transit using PAKE (Password Authenticated Key Exchange). When transfers fail or connections timeout, understanding the underlying architecture of the schollz/croc repository helps you pinpoint whether the issue lies in the relay layer, TCP engine, or CLI configuration. This guide walks through the most frequent failure modes and their exact fixes using the source code implementation.
Understanding croc's Architecture
croc relies on five distinct components that work together to establish encrypted transfers. Knowing these boundaries makes it easier to isolate problems.
- CLI (
src/cli/cli.go): Parses flags, builds thecroc.Optionsstruct, and launches the appropriate mode (send, receive, relay, or serve). - TCP Engine (
src/tcp/tcp.go): Handles the actual data streams between peers, performing the PAKE handshake, encryption, and resumable transfers. - Relay Layer (
src/croc/croc.go): Acts as a lightweight signaling proxy when direct connections fail (NAT traversal). It also manages the local relay unless the--no-localflag is used. - Web Relay (
src/webrelay/webrelay.go): Serves the browser-based UI and provides a WebSocket bridge to the relay. - Utility Package (
src/utils/utils.go): Provides helpers for IP discovery, configuration handling, hashing, and filename validation.
Fixing croc Relay Connection Failures
Resolving Port Conflicts (9009-9013)
The most common connection failure occurs when the default relay ports are already in use. You will see an error like:
error listening on 127.0.0.1:9009: bind: address already in use
According to the source code in src/tcp/tcp.go at line 145, the tcp.Run function returns fmt.Errorf("error listening on %s: %w", addr, err) when the listener cannot bind. To resolve this, specify free ports using the --ports flag:
croc --ports 9100,9101,9102 send file.txt
Verify port availability first:
nc -zv 127.0.0.1 9009-9013
Configuring Custom Relay Addresses
If you specify an incorrect relay address, the client cannot reach the signaling server. The validation logic in src/webrelay/webrelay.go at line 159 (webrelay.normalizeConfig) errors when the host is empty or malformed. Always verify your relay configuration:
croc --relay myrelay.example.com send file.txt
Alternatively, set the CROC_RELAY environment variable to ensure consistency across sessions.
Handling Firewall and Proxy Blocking
Outbound TCP or WebSocket traffic may be filtered by corporate firewalls. The TCP client uses net.Dial inside tcp.ConnectToTCPServer, and connection failures bubble up to the CLI as timeout errors. To bypass restrictions, route traffic through a SOCKS5 proxy:
croc --socks5 127.0.0.1:9050 send file.txt
Ensure the proxy supports TCP tunneling by testing it first:
nc -x 127.0.0.1:9050 -zv example.com 443
Local Relay Startup Issues (--no-local)
When you use the --no-local flag, croc disables the embedded relay and requires an external relay to be reachable. The CLI decides whether to start a local relay in src/cli/cli.go between lines 660-670. If you see connection timeouts while using --no-local, either remove the flag to enable the local relay or provide a valid external relay address with --relay.
Resolving croc Transfer and Authentication Errors
Missing CROC_SECRET Environment Variable
If the transfer fails immediately with "no secret provided" or the secret appears in the process list (a security issue tracked in CVE-2023-43621), the CLI cannot read the CROC_SECRET environment variable. The relevant logic in src/cli/cli.go (lines 120-130) shows that the --classic flag enables permanent environment-variable mode. Fix this by exporting the variable before running croc:
export CROC_SECRET=code-phrase
croc receive
To enable this behavior permanently, run:
croc --classic
Bypassing File Overwrite Prompts
By default, croc asks before overwriting existing files or resuming partial transfers. In src/cli/cli.go at line 141, the --overwrite boolean flag controls this behavior. To force overwrite without interaction, use:
croc --yes --overwrite <code-phrase>
To resume automatically without prompts (but keep existing files), use:
croc --yes <code-phrase>
Fixing Invalid File Name Errors
You may encounter errors stating basename cannot contain path separators or filename cannot be an absolute path. The utils.ValidateFileName function in src/utils/utils.go (lines 954-959) validates each argument before transfer. These errors trigger when you provide absolute paths or embed directory separators in a single filename argument. Always use relative paths without leading slashes:
croc send ./myfile.txt
Hash Mismatch and Resume Failures
If a resumed transfer aborts with unexpected EOF or hash mismatch, the source file likely changed while the transfer was paused. croc computes per-chunk hashes (SHA-256, XXHash, or IMO-hash) and stores state in temporary .croc files. The resume logic in src/comm/comm.go (referenced at line 27) verifies these hashes, and any discrepancy causes failure. To resolve:
- Ensure the source file is not being modified during transfer.
- Delete the partially-received
.crocfile on the receiver side and restart the transfer.
Troubleshooting croc Web Client and Network Issues
Web Relay Connection Problems
When accessing the embedded web UI at http://localhost:5173, you may see "relay unavailable". The webrelay.Handler function in src/webrelay/webrelay.go (line 238) checks that the requested port is in the allowlist; otherwise, it returns HTTP 403. Ensure the --ports flag includes the port you intend to use:
croc serve --ports 9009,9010
Verify the browser can reach the WebSocket endpoint:
curl -I https://croc.schollz.com/ws
SOCKS5 Proxy Configuration
If croc --socks5 "127.0.0.1:9050" hangs indefinitely, the proxy is either unreachable or does not support TCP tunneling. The proxy handling occurs in src/tcp/tcp.go at line 279 during the relay handshake. Test the proxy with nc as shown above, or remove the --socks5 flag to connect directly.
Using Debug Logging to Diagnose croc
When standard troubleshooting does not reveal the issue, enable verbose logging. In src/cli/cli.go at line 134, the --debug flag sets the global logger to "debug" level (lines 255-257). This output includes detailed connection attempts, handshake status, and low-level TCP errors:
croc --debug send myfile.txt
Quick Diagnostic Checklist for croc
Use this checklist to systematically eliminate common problems:
- Relay port conflict: Run
nc -zv localhost 9009-9013to verify ports are free. - Missing secret: Run
export CROC_SECRET=code-phrasebefore receiving. - Overwrite prompts: Add
--yes --overwriteto skip interaction. - Invalid filename: Use relative paths without leading
/characters. - Resume hash mismatch: Delete
.crocstate files and retry. - Web relay blocked: Ensure the port is in the allowlist with
--ports. - Proxy unreachable: Test connectivity with
nc -xbefore using--socks5. - Need detailed logs: Append
--debugto any command.
Summary
- Port conflicts occur when 9009-9013 are occupied; use
--portsto specify alternatives. - Authentication failures happen when
CROC_SECRETis not exported; use--classicto enforce this permanently. - Overwrite prompts can be bypassed with
--yesand--overwriteflags defined insrc/cli/cli.go. - Filename validation rejects absolute paths and separators; use relative paths only.
- Hash mismatches during resume indicate source file modification; delete
.crocfiles to restart. - Debug logging via
--debugprovides detailed diagnostics from the TCP engine and relay layer.
Frequently Asked Questions
Why does croc fail with "address already in use"?
This error originates in src/tcp/tcp.go when the default relay ports (9009-9013) are occupied by another process. Specify unused ports with --ports 9100,9101,9102 or stop the conflicting service.
How do I prevent my secret from appearing in the process list?
Export the CROC_SECRET environment variable before running croc, or enable --classic mode (defined in src/cli/cli.go lines 120-130) to force environment-variable usage and prevent the secret from appearing as a command-line argument.
Why does croc show "hash mismatch" when resuming a transfer?
This occurs when the source file changes after the initial transfer attempt. The resume logic in src/comm/comm.go validates per-chunk hashes stored in .croc state files. Delete the partial .croc file on the receiver and restart the transfer with a stable source file.
How do I enable verbose logging to debug connection issues?
Add the --debug flag to any croc command. This sets the log level to debug in src/cli/cli.go (line 134) and outputs detailed information about the PAKE handshake, relay negotiation, and TCP connection attempts.
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 →