| Heartbeat timeout | Increase agent --heartbeat interval or server --session-timeout |
| Erlang VM crash | Check /var/log/yuzu/gateway-crash.dump |
Gateway container keeps restarting at boot on an Intel host (docker ps shows Restarting (134)); the log has sys_sigaltstack(): Internal error: Failed to set alternate signal stack | The CPU supports AMX (Sapphire Rapids or newer, e.g. AWS c7i/m7i/r7i; grep -m1 -o amx_tile /proc/cpuinfo prints amx_tile) and the yuzu-gateway image is 0.13.0 through 0.14.0-rc4, which run on Alpine 3.24 (#2150). Pull an image that carries the #2150 fix (0.14.0-rc5 or later). Until you can, run the gateway on a host without AMX. A single-node gateway can instead use the yuzu-gateway-chisel image of the same version (glibc, unaffected); do not use it for a clustered gateway, because it lacks the entrypoint that resolves the node’s cluster address, so the node starts but never joins. In a gateway cluster, a node that is Down while the others report the cluster partially formed may be this. Rolling back the gateway to 0.13.x does not help on such a host: it is affected too. |
| Upstream unreachable | Verify server --gateway-upstream address matches gateway config. If the server did not start, or calls reach it and are answered with status 16 (UNAUTHENTICATED), the cause is gateway peer authorization (pin or acknowledgement), described in the rows below |
Server exits at boot after --gateway-upstream is set, with a gateway peer authorization error | The gateway-upstream service requires an authorized gateway peer. On the server’s generated default certificates the default gateway certificate is pinned automatically. Otherwise supply --gateway-peer-pin or --gateway-peer-pin-file; a plaintext (--no-tls) rig must pass --insecure-gateway-peer (development only), and --no-tls alone is refused. If you run no gateway, omit --gateway-upstream instead. See Gateway upstream peer authorization |
Gateway is up and TLS works but upstream calls fail with UNAUTHENTICATED (status 16) | The gateway’s key is not pinned on the server, or its certificate is expired or lacks serverAuth. A pin refusal is not a TLS-handshake failure: the session completes and each call is answered with status 16, so the gateway shows upstream RPC errors (yuzu_gw_upstream_rpc_errors_total{code="16"}, the YuzuGatewayUpstreamAuthRefused alert if the gateway is scraped), not yuzu_gw_upstream_tls_handshake_failures_total (a bring-your-own gateway leaf without clientAuth is the exception: it fails the handshake and raises YuzuGatewayUpstreamTlsHandshakeFailures, with no server-side signal). On the server yuzu_server_gateway_peer_denied_total{reason=...} rises (the critical YuzuGatewayPeerDeniedBatchHeartbeat means the gateway’s own heartbeat is refused: its registrations and stream-status notices fail, while established agent streams to the gateway stay up), and session.gateway_peer_denied audit rows are written for peers whose certificate parsed. Which cause class you are in decides whether any gateway is restarted, not the circuit state. Cause on the SERVER side (a wrong or missing pin or acknowledgement; the gateway’s certificate unchanged, class A): pin the gateway leaf’s key (--gateway-peer-pin, --gateway-peer-pin-file; explicit pins replace the automatic one) or add the acknowledgement, and restart the serving replicas; do not restart the gateways first, unless the placement check fails, because on agents without the #5183 fix (shipped in 0.15.0) a gateway restart leaves released agents wedged. Only on a gateway that has the #1197 consumer (#5383) does the gateway then recover through its replay path. To roll the change back, revert it on every replica and restart the replicas (the pin set is fixed at boot). Verify three separate things: sum(yuzu_gw_agents_current) - sum(yuzu_agents_connected{job="yuzu-server"}) settles at zero or below (summed across all gateway nodes and replicas; the server gauge also counts direct agents and counts registration, not placement), no rise in the raw yuzu_gw_upstream_rpc_errors_total{rpc_name="notify_stream",code="16"} or yuzu_gw_upstream_notify_dropped_total, and a read-only command to a sample agent returns a result (the exact request is in the runbook); agreeing counts alone do not prove placement. Cause on the GATEWAY side (certificate expired or renewed, key rotated, a renamed default-marker.json, class B): the gateway presents its current files only when it redials, and a server restart or any dropped connection forces one, so first check whether it already has: the raw counters yuzu_server_gateway_peer_denied_total and yuzu_gw_upstream_rpc_errors_total{code="16"} have stopped rising (read twice, a minute apart, not through increase()), no new spki= denial lines, and the configured certificate file has the expected SPKI prefix. A same-key renewal needs no server restart, and a redial can still present the old certificate for about 2 minutes after the files change (Erlang ssl PEM cache; measured on OTP 28.4.2 with a stub by a reviewer, not on a Yuzu gateway). Restart that gateway node, one at a time, only if denials continue after that wait and its agents can take the disconnect (unless the agents run a build with the #5183 fix, released agents stay wedged afterwards). Placement loss after a refusal window (class C): restart the agent service on the specific unreachable agents, which Gateway records as the observed fix, and restart the gateway node only if many agents behind one node are affected and the checks still fail after the timing allowances in the runbook (inferred and untested). The circuit state is not a gate: /readyz reports circuit_breaker true for both closed and half open, and 503 is also returned for other failed checks. Unless its agents run a build with the #5183 fix, a gateway restart disconnects every agent it holds and leaves released agents wedged: see Gateway, “What happens when the server restarts”. See the incident runbook in Gateway upstream peer authorization |
not_pinned and you cannot tell which side is wrong | Compute the SPKI hash of the certificate the gateway presents (the command under “Pinning your own gateway” in Gateway upstream peer authorization) and compare its first 16 hex characters with the spki= in the server’s gateway peer denied: ... reason=not_pinned spki=<16 hex> line and with the pin prefixes (first 16 hex) in the server’s boot line gateway peer authorization: enforcing N pin(s) ... (read it on every replica). Values the server refuses at boot: the colon-separated openssl x509 -fingerprint form, a SHA256 Fingerprint=, SHA2-256(stdin)= or sha256: prefix, 63 or 65 characters. The silent mistake that boots and then fails not_pinned is a hash of the whole certificate instead of its public key |
outside_validity on every call although the certificate was renewed | The guard re-checks the certificate on every call, but the gateway keeps its long-lived connection and presents the certificate it had when it dialled; status 16 does not make it redial. Renew before notAfter, then check whether the gateway already redialled (cause class B in the row above: restart that node, one at a time, only if denials continue after about 2 minutes and its agents can take the disconnect, because unless the agents run a build with the #5183 fix, shipped in 0.15.0, released agents stay wedged afterwards). A renewal over the same key needs no pin change and no server restart; a new key needs its pin first |
| Process limit | Erlang default process limit is 262144 — sufficient for most fleets |