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 the croc.Options struct, 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-local flag 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:

  1. Ensure the source file is not being modified during transfer.
  2. Delete the partially-received .croc file 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-9013 to verify ports are free.
  • Missing secret: Run export CROC_SECRET=code-phrase before receiving.
  • Overwrite prompts: Add --yes --overwrite to skip interaction.
  • Invalid filename: Use relative paths without leading / characters.
  • Resume hash mismatch: Delete .croc state files and retry.
  • Web relay blocked: Ensure the port is in the allowlist with --ports.
  • Proxy unreachable: Test connectivity with nc -x before using --socks5.
  • Need detailed logs: Append --debug to any command.

Summary

  • Port conflicts occur when 9009-9013 are occupied; use --ports to specify alternatives.
  • Authentication failures happen when CROC_SECRET is not exported; use --classic to enforce this permanently.
  • Overwrite prompts can be bypassed with --yes and --overwrite flags defined in src/cli/cli.go.
  • Filename validation rejects absolute paths and separators; use relative paths only.
  • Hash mismatches during resume indicate source file modification; delete .croc files to restart.
  • Debug logging via --debug provides 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:

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 →