> ## Documentation Index
> Fetch the complete documentation index at: https://www.floe.one/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Floe Troubleshooting Guide

> Fixes for common Floe issues: stuck connections, failed transfers, invalid room links, TURN relay problems, and self-hosting deployment errors.

Most Floe transfers just work. When something goes wrong, it is almost always one of the cases below.

## Sending and receiving

<AccordionGroup>
  <Accordion title="The page says my browser is not supported">
    Floe needs WebRTC, which is unavailable in some in-app browsers (the web views embedded in Facebook, Instagram, TikTok, and similar apps). Open the link in a real browser instead: Chrome, Safari, Edge, or Firefox. Floe detects these in-app browsers and prompts you to switch.
  </Accordion>

  <Accordion title="The transfer never starts or is stuck connecting">
    Both peers must be present at the same time, and the connection is direct, so a few things can hold it up:

    * The sender must keep their browser tab open for the entire transfer. If the sender closes the tab, the session ends.
    * The recipient must actually open the shared link (or run `floe receive`). The transfer begins automatically once both sides connect.
    * On strict networks, a direct path may not be possible. Make sure **Network Relay Fallback** is enabled (it is on by default) so Floe can route through the encrypted relay.
  </Accordion>

  <Accordion title="Transfer blocked: relay limit exceeded">
    Relay connections are capped at **2 GB per session** to keep the service free. Direct connections have no size limit. To get a direct connection:

    * Use a standard home or personal Wi-Fi network.
    * Disconnect from any VPN.
    * Avoid strict corporate or university networks.
    * Try a personal mobile hotspot.

    See [The 2 GB Relay Limit](/docs/how-it-works/2gb-limit) for the details.
  </Accordion>

  <Accordion title="The transfer is slow">
    Check the connection indicator. **Green (Direct)** is as fast as the slower of the two connections. **Amber (Relay)** routes through a TURN server, so speed depends on relay load and network conditions. To force the faster direct path, follow the steps above to obtain a direct connection.
  </Accordion>

  <Accordion title="Downloads fail or behave oddly on iOS">
    On iOS, use **Download ZIP** rather than downloading files individually. It is the most reliable option on Safari and iOS web views.
  </Accordion>

  <Accordion title="My short code does not work">
    Short codes expire **10 minutes** after the sender starts the session. After that, only the full link works, and only while the sender is still connected. Ask the sender to start a new session for a fresh code.
  </Accordion>
</AccordionGroup>

## CLI

<AccordionGroup>
  <Accordion title="The CLI cannot reach the server">
    The CLI connects to `https://api.floe.one` by default. If you are using a self-hosted instance, pass `--server` with your signaling server URL, and make sure **both** the sender and receiver use the same value. See [Against a Self-Hosted Server](/docs/cli/self-hosted-server).
  </Accordion>

  <Accordion title="A browser recipient cannot join my CLI transfer">
    A browser can only join by opening the link, not by entering a short code (there is no code field in the web UI). Share the **link** printed by `floe send` with browser recipients, and the **code** with CLI recipients.
  </Accordion>

  <Accordion title="The CLI is slow to connect when a VPN or virtual machine is running">
    A VPN (such as Tailscale) or virtualization software (VMware, VirtualBox, WSL, Hyper-V) adds extra network interfaces. The CLI probes each one while establishing the connection, so a machine with several of them can spend longer on "Connecting..." before settling on the working path. The transfer still completes, and the browser app is unaffected.

    The CLI already skips dead auto-config interfaces automatically. If you still see slow connects, pin the CLI to your real interface by name:

    ```bash theme={null}
    floe receive olive-tiger-castle --iface Ethernet   # or --iface Wi-Fi
    ```

    Pass `--iface` more than once to allow several interfaces. Temporarily quitting the VPN during a transfer also resolves it.
  </Accordion>

  <Accordion title="The CLI says the version is incompatible">
    Peers on different floe versions normally transfer fine, so a small version difference (for example v1.5.4 and v1.5.5) is never a problem. This message only appears if a release changed the transfer protocol in a way the two versions cannot bridge. Whichever side is older should update:

    ```bash theme={null}
    floe update              # script / manual installs
    brew upgrade floe        # Homebrew
    winget upgrade jannskiee.floe  # Winget
    scoop update floe        # Scoop
    ```

    If you are receiving from a browser, refresh the page to load the latest web client. See [Updating](/docs/cli/update).
  </Accordion>
</AccordionGroup>

## Self-hosting

<AccordionGroup>
  <Accordion title="The browser cannot connect to my signaling server">
    Set `NEXT_PUBLIC_SOCKET_URL` in `.env` and recreate the container. No rebuild is needed, because the client reads its server address at runtime:

    ```bash theme={null}
    docker compose up -d
    ```

    Check what the client is actually resolving:

    ```bash theme={null}
    curl http://localhost:3000/api/config
    ```

    An empty `socketUrl` means the browser will use its own origin, which is correct only when both services sit behind [one reverse proxy](/docs/self-hosting/reverse-proxy). Otherwise confirm the URL is reachable from the end user's browser: `http://localhost:3001` only works when the browser runs on the same machine as the server. See [how the client finds the server](/docs/self-hosting/build-time-url).
  </Accordion>

  <Accordion title="Browser requests are blocked by CORS">
    Set `CLIENT_URL` to your client's public origin (e.g. `https://app.your-domain.com`). It is added to the server's CORS allow-list. `http://localhost:3000` is already allowed by default. See [Production Deployment](/docs/self-hosting/production).
  </Accordion>

  <Accordion title="I am being rate limited (429 or Rate limit exceeded)">
    The server allows 30 connections per IP per 60 seconds (Socket.IO and WebSocket), 20 requests per IP per 60 seconds for TURN credentials, and 60 reports per IP per 60 seconds for the global stats counter. Behind a reverse proxy, set `TRUSTED_PROXY_COUNT` to the number of proxy hops so the server reads real client IPs instead of the proxy IP. See [Configuration](/docs/self-hosting/configuration).
  </Accordion>

  <Accordion title="The TURN relay is not working">
    First check which relay option your instance uses.

    **Managed Cloudflare TURN:** confirm both `CLOUDFLARE_TURN_KEY_ID` and `CLOUDFLARE_TURN_KEY_API_TOKEN` are set in `.env`, then run `curl http://localhost:3001/api/turn-credentials`. A working setup returns `turn.cloudflare.com` entries with a username and credential. If it returns STUN servers only, one of the two variables is missing or the API token is invalid (roll it in the Cloudflare dashboard under Realtime, then TURN Server).

    **Self-hosted coturn:** the bundled coturn service uses host networking, so it runs on a **Linux host** with a public IP, a domain, and TLS certificates. On macOS or Windows with Docker Desktop, run coturn separately or on a Linux VM. Confirm that `TURN_SECRET` and `TURN_DOMAIN` in `.env` match coturn's `static-auth-secret` and `realm`, then start the stack with `docker compose --profile turn up -d`. See [TURN Relay](/docs/self-hosting/turn-relay).
  </Accordion>
</AccordionGroup>

## Still stuck?

If none of the above helps, open an issue on [GitHub](https://github.com/jannskiee/floe/issues) with your browser and OS, whether the connection was direct or relay, and any error message you saw.
