---
audience: admin
summary: Invites to / from another server don't go through. Events return signature
  errors.
title: Federation handshake fails
path: troubleshooting/federation-handshake-fails
status: published
---

# Federation handshake fails

## Symptom: Invite POST returns 4xx

You try to invite a foreign user; the POST to your local API
returns an error or the foreign side returns 4xx.

### `SW_FED_PEER_UNKNOWN`

Your server doesn't know the foreign server (or vice versa).
Either:

- Add the peer to `allowed_peers` in your federation policy.
- Or set `default_mode: "open"` if you trust open peering.

```bash
curl -X PUT https://your-host/v1/admin/federation/policy \
  -H "Authorization: Bearer $ADMIN" \
  -d '{"allowed_peers": ["peer.example.com"]}'
```

The foreign side needs the same setup pointed at *your* host.

### `SW_FED_SIGNATURE_MISMATCH`

The event arrived with a bad signature. Possible causes:

- **Foreign server rotated its key**, your server has the old one
  cached. Refresh the foreign participant's profile:

  ```bash
  curl -X POST https://your-host/v1/admin/federation/foreign/$FED_PARTICIPANT_ID/refresh \
    -H "Authorization: Bearer $ADMIN"
  ```

- **Time skew** between servers. Federation signatures include a
  timestamp; if clocks drift > 60s, signatures appear invalid.
  Sync NTP on both sides.

- **TLS chain on the well-known endpoint changed**. Your server
  fetches `/.well-known/scaiwave/server` over TLS to get the
  public key; if their cert chain has a chain issue your server
  won't trust the descriptor.

### `SW_FED_PROTOCOL_VERSION`

The two servers run incompatible protocol versions. Federation is
versioned (`v1`, future `v2`); both sides must speak at least one
common version. Check:

```bash
curl https://your-host/.well-known/scaiwave/server | jq .protocol_versions
curl https://peer.example.com/.well-known/scaiwave/server | jq .protocol_versions
```

Intersection must be non-empty.

## Symptom: Invite accepted but no events flow

Foreign user is in the member list but their messages don't
appear (or yours don't reach them).

- Check `GET /v1/admin/federation/peers` — the row for the peer
  has `last_error` and `last_seen`. If `last_error` is set, that's
  the reason.
- The relay worker may be stuck. Restart the ARQ worker pods.
- The foreign side may have you on a deny list. Their admin
  checks their policy.

## Symptom: Backfill returns empty

```bash
curl -X POST https://your-host/v1/admin/rooms/$ROOM_ID/federation/backfill
```

Returns 200 with 0 events. Causes:

- Their server's policy refuses backfill to your server (their
  `max_backfill_events_per_request` is 0, or their policy hides
  history).
- The room isn't federated on their side (only on yours, by
  accident). Both sides have to set the per-room federate flag.

## Symptom: Federation entirely silent

You set everything up; no errors, no events flow.

`GET /v1/admin/federation/peers` shows the peer with
`events_sent_24h: 0, events_received_24h: 0`. Likely:

- TLS handshake to the peer is failing silently. Check your
  server's outbound logs for `https://peer.example.com` requests.
- The peer's `/well-known` is reachable but their `/send` endpoint
  returns 404 (e.g. they removed the federation feature). They'd
  need to check.

## Where to look

Logs: `federation.*` events. Useful greppable names:

- `federation.send.success` / `federation.send.failed`.
- `federation.signature_verify_failed`.
- `federation.peer_discovery_failed`.
- `federation.policy_rejected`.

The federation event audit also has a row per refused / accepted
event in `sw_audit` under `action = federation.*`.
