Problem
With a gateway reached over Tailscale, every transfer through the tunnel runs at roughly 0.6–1.5 KB/s and any response larger than about 16KiB never completes. Small requests succeed, so the deployment looks healthy until something tries to move real data.
The gateway logs this continuously while traffic flows:
ERROR: [wg] peer(...) - Failed to send data packets: write udp 0.0.0.0:51820: sendmmsg: message too long
EMSGSIZE here is WireGuard forming datagrams larger than the Tailscale path can carry: the Tailscale interface is MTU 1280, while wg's tun defaults to 1420. Every full-size packet is dropped at the socket, and nothing adapts.
Measurements
Same URL, same client, with and without clawpatrol run. Both hosts are unbrokered (no endpoint declared, no credential injected), so neither the MITM path nor policy evaluation is involved:
| request |
bytes |
direct |
through gateway |
| a JSON API response |
6,868 |
0.26s (26.7 KB/s) |
6.60s (1.0 KB/s) |
| a raw file over HTTPS |
3,807 |
0.23s (16.7 KB/s) |
5.91s (0.6 KB/s) |
A size sweep stalls at a fixed ceiling rather than degrading gradually. Three different objects stopped at ~15.59KB, and one JSON response stopped at exactly 16,384 bytes:
| object |
bytes received |
outcome |
| 7.7KB thumbnail |
7,731 |
completed in 4.9s |
| 20KB thumbnail |
20,449 |
stalled at 20s timeout |
| larger thumbnails (x2) |
15,587 / 15,594 |
stalled |
| 1.3MB file |
15,593 |
stalled |
| JSON listing |
16,384 |
stalled |
Why the error rate is misleading
The sendmmsg errors arrive at a flat ~3.5/second regardless of what the traffic is doing — 36 errors across five small API calls versus 68 across twenty seconds of bulk download. That flatness reads as harmless background noise, which is how this went unnoticed through several rounds of functional testing. It is flat because it is paced by TCP retransmission backoff, not by transfer volume.
This is the same size-dependent black hole class described in #787, but on the IPv4 full-tunnel path with a WireGuard-over-Tailscale transport, and with the drop occurring on the gateway's own send path rather than on an unhandled ICMP signal.
What we tried
MTU = 1200 in the client's wg.conf [Interface] — no effect. Per-process mode uses a userspace netstack rather than bringing up the kernel interface via wg-quick, so the file's MTU line appears not to be consulted.
- Searching the config schema for an MTU attribute:
mtu is rejected in gateway, gateway.wireguard, defaults and endpoint blocks, and does not appear among the binary's HCL tags.
- Searching for an environment override: no
CLAWPATROL_* variable exposes an MTU.
- Capping stored response bodies (
limits.body_storage), in case audit persistence was the bottleneck — no change, which is what ruled that out.
So there appears to be no way to configure around this from the deployment side.
Suggested directions
Any one of these would unblock it:
- A configurable WireGuard MTU in the
gateway.wireguard block, pushed to clients through the config channel so both ends agree.
- Automatically clamping the tun MTU when the transport is tsnet/Tailscale, where 1280 is the known path MTU.
- Honoring
wg.conf's MTU in per-process mode, so operators at least have a client-side lever.
Happy to test a patch or collect more data — the reproduction is reliable and takes seconds.
Environment
- Claw Patrol 0.5.8
- Gateway: Linux x86_64, kernel 6.19, systemd unit, userspace WireGuard on udp/51820, reached over Tailscale
- Client: Linux, per-process mode (
clawpatrol run), joined without --whole-machine
- Tailscale interface MTU 1280 on both ends; direct (non-relayed) path between them
Problem
With a gateway reached over Tailscale, every transfer through the tunnel runs at roughly 0.6–1.5 KB/s and any response larger than about 16KiB never completes. Small requests succeed, so the deployment looks healthy until something tries to move real data.
The gateway logs this continuously while traffic flows:
EMSGSIZEhere is WireGuard forming datagrams larger than the Tailscale path can carry: the Tailscale interface is MTU 1280, while wg's tun defaults to 1420. Every full-size packet is dropped at the socket, and nothing adapts.Measurements
Same URL, same client, with and without
clawpatrol run. Both hosts are unbrokered (no endpoint declared, no credential injected), so neither the MITM path nor policy evaluation is involved:A size sweep stalls at a fixed ceiling rather than degrading gradually. Three different objects stopped at ~15.59KB, and one JSON response stopped at exactly 16,384 bytes:
Why the error rate is misleading
The
sendmmsgerrors arrive at a flat ~3.5/second regardless of what the traffic is doing — 36 errors across five small API calls versus 68 across twenty seconds of bulk download. That flatness reads as harmless background noise, which is how this went unnoticed through several rounds of functional testing. It is flat because it is paced by TCP retransmission backoff, not by transfer volume.This is the same size-dependent black hole class described in #787, but on the IPv4 full-tunnel path with a WireGuard-over-Tailscale transport, and with the drop occurring on the gateway's own send path rather than on an unhandled ICMP signal.
What we tried
MTU = 1200in the client'swg.conf[Interface]— no effect. Per-process mode uses a userspace netstack rather than bringing up the kernel interface viawg-quick, so the file's MTU line appears not to be consulted.mtuis rejected ingateway,gateway.wireguard,defaultsandendpointblocks, and does not appear among the binary's HCL tags.CLAWPATROL_*variable exposes an MTU.limits.body_storage), in case audit persistence was the bottleneck — no change, which is what ruled that out.So there appears to be no way to configure around this from the deployment side.
Suggested directions
Any one of these would unblock it:
gateway.wireguardblock, pushed to clients through the config channel so both ends agree.wg.conf'sMTUin per-process mode, so operators at least have a client-side lever.Happy to test a patch or collect more data — the reproduction is reliable and takes seconds.
Environment
clawpatrol run), joined without--whole-machine