# Troubleshooting

> Fix common Frame server problems: the app can't connect, invites don't work, HTTPS or certificate errors, failed uploads, slow live updates, rate limits, missing Apple or Google sign-in and notifications.

Source: https://crow.sg/apps/frame/docs/troubleshooting/

First look at the server's own log: `journalctl -u frame -n 100` with the Linux installer, or `docker compose logs --tail 100 frame` with Docker. `systemctl status frame` tells you whether it is running.

## The app says it can't connect

1. On your phone's browser, open `https://your-domain/api/v1/server`. You should see JSON with your server's name.
2. If the page doesn't load:
   - DNS: does `dig +short your-domain` show your server's IP?
   - Firewall: `sudo ufw status` should allow 80 and 443. Check the provider's firewall too.
   - Service: run `systemctl status frame` and `journalctl -u frame -n 50`.
   - Tunnel: is `cloudflared` running (`systemctl status cloudflared`), and is the public hostname set to `localhost:8080` (or `http://frame:8080` with Docker)?
3. In the app, type the address without `https://` and without a trailing slash, such as `frame.example.com`. The app adds the rest.

## A friend can't sign up

"Invite required" or "invite invalid": the server is invite-only. Send them a link from **Invites → New invite** in the admin panel, or have a member share theirs from the app. "Invalid" means the link was revoked, expired or used up (server invites are single-use by default), or that **Members can invite** is off and they used a member's link. Check **Settings** and **Invites** in the panel.

## Lost the setup link, or locked out of the panel

Run `sudo frame-admin setup-link` while no admin exists. Otherwise: `sudo frame-admin user reset-password <you>` for a forgotten password, `sudo frame-admin user reset-2fa <you>` for a lost authenticator, or `sudo frame-admin user promote <you>` to make an existing account an admin. With Docker, use `docker compose exec frame frame-server admin …`.

## HTTPS or certificate errors

- With `FRAME_DOMAIN`, Let's Encrypt must reach ports **80 and 443** on your server directly. If the DNS record is proxied by Cloudflare (orange cloud), switch it to **DNS only**, or use a [Cloudflare Tunnel](https://crow.sg/apps/frame/docs/install-systemd/#cloudflare-tunnel) instead.
- Did you run `sudo sh install.sh --reconfigure` after setting `FRAME_DOMAIN`? Without it, Frame can't bind ports 80/443, and the log shows "permission denied".
- Let's Encrypt rate limits failed attempts. If you retried many times, wait an hour.
- The apps reject self-signed certificates. Use a real domain.

## Uploads fail or videos won't send

A proxy in front is probably limiting the request size. Caddy: `request_body max_size 64MB`. nginx: `client_max_body_size 64m`. Cloudflare's free plan: 100 MB at most. Also check `FRAME_MAX_UPLOAD_MB`.

## New messages arrive slowly behind a proxy

The proxy is buffering `/api/v1/events`, a Server-Sent Events stream. Turn buffering off for that path (nginx: `proxy_buffering off;` and `proxy_read_timeout 1h;`); see [Behind Caddy, nginx or another proxy](https://crow.sg/apps/frame/docs/install-systemd/#reverse-proxy).

## Everyone gets "too many requests"

Or rate limits hit the wrong people: Frame sees your proxy's IP instead of the visitors'. Set `FRAME_TRUSTED_PROXIES` to match your setup:

| Setup | Value |
|---|---|
| cloudflared on the same host | `cloudflare,127.0.0.1/32` |
| Caddy or nginx on the same host | `127.0.0.1/32,::1/128` |
| Docker with Caddy | preset in the compose file |
| Docker with a tunnel or a host proxy | the (b) or (d) line in `docker-compose.yml` |

`cloudflare` on its own trusts nobody: it must be paired with the address the tunnel connects from.

## Sign in with Apple or Google is missing

Set `FRAME_PUBLIC_URL` to the exact address people type into the app, then restart. If people use more than one address, list them all, separated by commas.

## Notifications don't arrive

- Check that `FRAME_PUSH_RELAY` isn't empty.
- Check that the server can reach the relay: `curl -sI https://frame.crow.sg/healthz`.
- On the phone, allow notifications for Frame. On Android, also turn off battery optimisation for Frame. On iOS, check Focus modes.
- Look for `push failed` in the logs.

## "Permission denied" or "readonly database" after admin commands

You probably ran `frame-server` directly as root, which left root-owned files in the data directory. Give them back to the service and restart, and from then on use `sudo frame-admin …`:

```sh
sudo chown -R --reference=/var/lib/private/frame /var/lib/private/frame && sudo systemctl restart frame
```

## Testing on your computer, an emulator or the local network

Use `http://10.0.2.2:8080` in the Android emulator, `http://localhost:8080` in the iOS simulator, and your computer's LAN address (such as `http://192.168.1.20:8080`) from a phone on the same Wi-Fi, with `FRAME_LISTEN` not set to `127.0.0.1`. Dev mode uses port 8787. See [Try it locally](https://crow.sg/apps/frame/docs/install-binary/#dev).

Still stuck? Ask in the [GitHub repository](https://github.com/Crow-Tech-Pte-Ltd/frame/issues), or write to `hello@crow.sg`.
