Troubleshooting
The most common native-UDP problem is "my datagrams send fine but I never receive anything back." Because UDP is fire-and-forget, a send call succeeds even when the packet is black-holed or rejected — so silence is the only symptom. Work down this checklist in order.
"Data goes out, nothing comes back"
1. Did serverWithLeastClients actually succeed?
This query installs your Buddy session as a side effect — see
Authenticate and assign. If
it returns GraphQL errors, the resolver never ran and no session exists, so
every subsequent UDP datagram is dropped.
A frequent cause is requesting a field that isn't exposed (e.g. peerPort), which
fails the whole query. Request only valid fields and log the full response:
query { serverWithLeastClients { ip4 ip6 clientPort status clients } }
Confirm the response body has no errors and a server object with
status: "ReadyForClients".
2. Did you wait after assignment?
Session install is asynchronous. Wait ≥ 1.5 s after serverWithLeastClients
returns before your first UDP send. Earlier messages are silently dropped. See
Operations → recommended waits.
3. Are you sending to the right address and port?
- Send to
ip4onclientPort(typically9091). Prefer IPv4 — see Choosing an address. - A black-holed IPv6 route is a classic cause of "sends succeed, nothing returns."
If you addressed
ip6, switch toip4. - Spatial traffic goes to the client port, never the peer port.
4. Is your HMAC key correct?
This is the single most common client bug. The token is a 64-character string used directly as 64 octets — do not hex-decode it. Hex-decoding produces a 32-byte key and every message fails. See HMAC.
const key = Buffer.from(token, 'latin1'); // 64 bytes — correct
// const key = Buffer.from(token, 'hex'); // 32 bytes — WRONG, will fail
Also confirm:
containsAuth = 1on your first (and every authenticated) message.- The
gameTokenIdin the message tail matches the one from your app-scoped token (themintAppToken/exchangePortalCoderesponse) — not the identity session token fromlogin/register, which Buddy rejects. - The HMAC message is
prefix || token_octets(the token bytes are appended again after the header+payload).
5. Are you listening on the same socket you send from?
Buddy replies to the source address/port of your datagrams. If you send from one socket and try to receive on a different one (or a different local port), the replies never reach your handler. Use one socket for both directions, and accept datagrams from the Buddy host IP regardless of the exact source — a server may reply from a different internal interface than the one you addressed.
6. If everything above checks out — it's server-side
If you have a clean serverWithLeastClients (no errors, ReadyForClients), waited,
are sending HMAC-valid ACTOR_UPDATE_REQUEST_2 to ip4:clientPort, and listening on
the same socket — and still get nothing — the problem is in the environment, not your
client. Report it to your platform contact. Known server-side causes that produce
this exact symptom:
- Buddy's heartbeat is stale (it isn't registered as
ReadyForClients). - The game-api process is missing a required secret and can't complete the session-install packet.
- A version/routing mismatch between game-api and Buddy.
Error codes
If you receive a GENERIC_ERROR_MESSAGE (type 3) you are at least getting a reply —
decode the error code and see
Operations → common error codes.
INVALID_TOKEN (5) almost always means the HMAC key was hex-decoded (step 4).
INVALID_APP_ID (18) means the token is for a different app (app-scope mismatch)
or you assigned with the session token — re-mint for the correct app.
TOKEN_EXPIRED (32) means your app-scoped token passed its expiresAt and Buddy
evicted the session — mint a fresh one (refreshAppToken for the same app) and
re-run serverWithLeastClients.
USER_NOT_AUTHENTICATED (20) means the session isn't installed yet (steps 1–2)
— or the app is runtime-denied (suspended / out of wallet funds / over a
spend cap), in which case the server won't authorize the token until the studio
resolves it on the management API. See
Operations → app suspended or over budget.
Browser clients
If you cannot open raw UDP sockets, none of the above applies — use the GraphQL UDP proxy instead.