Troubleshooting
Diagnose startup, configuration, caller access, policy, upstream, and AISIX gateway failures in AISIX AI Gateway.
Narrow an AISIX failure to startup, configuration, caller policy, the provider path, or AISIX Enterprise projection before changing resources. Work through these checks in order until the failing layer is clear, then use the matching section to decide the next action.
Fast triage
Start with the runtime path before changing configuration.
Check listener health first:
curl -i "http://127.0.0.1:3000/livez"A refused connection here is a signal in its own right, not a failed command. With etcd or AISIX Enterprise as the resource source, the gateway does not bind the proxy listener until it has applied its first configuration, so an instance that has never reached that source has no proxy port to answer on. See The Gateway Runs but the Proxy Port Refuses Connections.
When Prometheus metrics are enabled, check the latest observed and applied configuration on the private metrics/status listener, which binds as soon as the process starts and therefore answers in that case too:
curl -sSi "http://127.0.0.1:9090/status/ready"
curl -sS "http://127.0.0.1:9090/status/config"Verify model discovery with the same caller API key the application uses:
AISIX_API_KEY="YOUR_CALLER_API_KEY"
curl -sS "http://127.0.0.1:3000/v1/models" \
-H "Authorization: Bearer ${AISIX_API_KEY}"Then send one real request to the endpoint that failed. For example, check the OpenAI-compatible chat path with the caller API key:
curl -sS "http://127.0.0.1:3000/v1/chat/completions" \
-H "Authorization: Bearer ${AISIX_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o-mini",
"messages": [
{
"role": "user",
"content": "Say hello."
}
]
}'Use the response status, error type, and correlation header to choose the next check.
| Signal | What to Check Next |
|---|---|
| Listener health is refused rather than answered. | Whether any configuration has been applied at all: /status/ready on the metrics/status listener, then the configuration source. |
| Listener health fails. | Process, listener binding, startup config, and TLS. |
Configuration status is degraded or out_of_sync. | Rejected resources, the last load failure, and the configuration source. |
| Configuration status does not reflect an expected direct etcd change. | etcd reachability, the watched prefix, and configuration source state. |
| Model discovery does not show the expected alias. | Caller API key access, model type, and configuration visibility. |
| Real proxy request fails before upstream dispatch. | Caller authentication, model access, guardrails, rate limits, or budgets. |
| Real proxy request reaches the provider and fails. | Provider key, base URL, upstream model ID, quota, outage, or outbound network path. |
For request correlation, use x-aisix-request-id, which AISIX adds to proxy responses across endpoint families. Successful Chat Completions responses also include x-aisix-call-id. For exact header scope, see Headers and Error Codes.
Verify Configuration Visibility
Use this step when resources were created or updated recently, or when the proxy behaves as if it has older configuration.
| Signal | Check | Action |
|---|---|---|
| Process fails during startup. | Dynamic-resource source, network reachability, TLS certificate paths, file permissions, and startup config syntax. | Fix the startup configuration or resource source, then restart the gateway. |
| The process runs but the proxy port refuses connections. | /status/ready and /status/config on the metrics/status listener, then whether the gateway can reach its configuration source. | Restore the configuration source. The gateway keeps retrying and binds the listener as soon as one read succeeds; see The Gateway Runs but the Proxy Port Refuses Connections. |
Configuration status is never_loaded, degraded, or out_of_sync. | /status/config fields source, applied, rejected, and last_failure. | Restore the source or correct rejected configuration, then confirm that the next load is synced. |
| A direct etcd change does not appear in proxy traffic. | Applied gateway configuration snapshot, store connectivity, and the watched prefix. | Verify the final proxy path after the snapshot updates; see Configuration Propagation. |
| Model discovery omits a new alias. | Caller API key allow list, model type, and snapshot freshness. | Correct the model or caller API key, then query model discovery again. |
| Error mentions a missing provider key or unknown resource. | Provider key, model, and caller API key references. | Create or correct dependent resources in order, then send a real proxy request. |
For a store-backed gateway, etcd provides the dynamic-resource source. If etcd becomes unavailable after a snapshot has loaded, the gateway can continue using that snapshot, but it cannot receive new or updated dynamic resources until the configuration connection recovers.
The Gateway Runs but the Proxy Port Refuses Connections
Use this step when the process is up — it has not exited and has reported no error — but every request to the proxy port, including /livez, is refused.
A gateway that reads its resources from etcd or from AISIX Enterprise binds the proxy listener only after it has applied its first configuration. Before then the port does not exist, so the failure looks like a process that never started rather than one that is waiting. It is genuinely waiting: it does not exit, does not bind a degraded listener, and does not fall back to serving nothing. The one exception is the boot connection itself being answered and refused — etcd credentials that are wrong, or credentials presented to a cluster with authentication disabled — which ends the boot with that error instead, because waiting cannot repair them. A refusal that arrives after that connection, a user without permission on etcd.prefix included, leaves the gateway in exactly this wait. It binds the listener the moment a read succeeds, and retries a read that fails on an exponential backoff that grows from one second to a sixty-second ceiling. See Startup and the First Configuration for the full contract.
Confirm the diagnosis on the metrics/status listener, which is bound throughout:
curl -sSi "http://127.0.0.1:9090/status/ready"
curl -sS "http://127.0.0.1:9090/status/config"A 503 with no configuration available, alongside state: never_loaded in /status/config, is this case. The gateway log carries it as well: one waiting for the first configuration before binding the proxy listener line when the wait starts, then proxy listener still not bound: no configuration has been applied yet every ten seconds until it ends.
| Signal | Check | Action |
|---|---|---|
/status/config reports source.connected: false and state: never_loaded. | etcd endpoints or the AISIX Enterprise endpoint, DNS resolution, mTLS material, and any network policy between the gateway and that source. | Restore reachability. The gateway binds without a restart once a read succeeds. |
/status/config reports state: empty and the listener is bound. | The configured etcd.prefix and env_id. | The source is reachable but holds no resources for this gateway; correct the prefix rather than the connection. |
The gateway log shows the ten-second proxy listener still not bound warning but /status/config records no connection failure. | Whether the configured endpoint accepts connections without answering — a load balancer in front of a dead configuration store behaves this way. | Fix the path to the source. The configuration read is unbounded unless etcd.request_timeout_ms is set, so by default a read that is accepted stays in flight and this case produces the warning without a visible failure to retry from. Setting that key bounds the read and turns the stall into a failure the backoff retries; size it against the guidance there, not against how long you are willing to wait. It covers the configuration read and the handshake that creates the configuration watch. A stall in the connect-time authentication is covered too — by etcd.dial_timeout_ms at boot, and by etcd.request_timeout_ms when a later call has to dial for itself — though both are unset by default, so nothing bounds it until you set one. When etcd.user is set, a dial stuck this way logs still connecting to etcd — nothing waiting on this connection can proceed until it answers every ten seconds as well, naming the endpoints it is waiting on. |
| The metrics/status listener is also refused. | Whether the process is running at all, observability.metrics.prometheus.enabled, and — when etcd.user is set — whether the gateway is still inside its first dial. | Usually not the wait: diagnose it as a process or startup-configuration failure. The exception is a gateway with etcd credentials and etcd.dial_timeout_ms unset, whose first dial runs before any listener is created; it logs still connecting to etcd — nothing waiting on this connection can proceed until it answers every ten seconds and opens no port meanwhile. Setting dial_timeout_ms bounds that dial and lets the gateway reach the wait above. |
| The process exited at startup reporting that etcd refused the connection. | The configured etcd.user, the password in etcd.password_env, and whether the cluster has authentication enabled at all. | This is not the wait either. An etcd it cannot reach is waited out; credentials refused on the boot connection end it deliberately, so that a wrong password does not surface as a gateway that is up and permanently empty. |
The gateway is running, the wait never ends, and the log repeats etcd refused this gateway's credentials — no configuration can be read until they are fixed; still retrying at ERROR. | That user's permission on etcd.prefix, and the credentials themselves if the gateway started while etcd was unreachable. | etcd checks permissions per call rather than while authenticating, so a refusal can arrive after the boot connection — and one that does is retried rather than fatal. /status/config looks the same here as it does for an unreachable source; this log line is what distinguishes them. |
In Kubernetes, the pod restarts repeatedly and reports CrashLoopBackOff. | The same reachability checks; the startup probe's budget is doing what it is meant to. | Restore the configuration source. Do not raise the budget to mask an unreachable source, and do not repoint the probes at another listener. |
A gateway that restored a usable snapshot from its on-disk cache, or one configured from a resources file, binds immediately and never reaches this state. See Offline Resilience.
Verify Caller Access and Policy
Use this step when AISIX rejects the request before a provider call, or when model discovery differs between caller API keys.
| Signal | Check | Action |
|---|---|---|
| Authentication error. | The application sends the plaintext caller API key, not the stored hash or upstream provider key. | Update the application secret or authorization header. |
| Permission or model-access error. | The requested model alias is allowed by the caller API key. | Add the alias to the caller API key or request an allowed model. |
| Content-policy error. | Enabled guardrails, the stage where each guardrail runs, and the triggering prompt or response content. Match the guardrail's kind and result labels to the specific error_type in Guardrail Metrics to tell a policy block from a fail-open bypass. | Adjust the prompt, guardrail rules, or fail-open behavior where appropriate. |
| Rate-limit or budget error. | Retry hint, API-key limit, model limit, shared policy, AISIX Enterprise budget state, and replica-local counters. | Wait for the retry window, increase the limit, or adjust the matching policy. |
For exact proxy error envelopes, status codes, and retry headers, see Proxy Errors and Retries.
When rate limits differ across gateway instances, identify the counter backend before changing a policy. Memory counters are local to each process. Redis shares counters, but a runtime Redis failure falls back to process-local enforcement. Outage-time counts are not reconciled into Redis after recovery, so restore Redis and let the active windows roll over before assuming the counters are aligned again.
Verify the Provider Path
Use this step after AISIX authenticates the caller, resolves the model alias, and starts dispatching to the configured provider.
| Signal | Check | Action |
|---|---|---|
502 or upstream_error. | Provider key secret, base URL, upstream model ID, provider quota, provider outage, and outbound network path. | Fix the provider path, then send the same proxy request again. |
503 with provider unavailable. | Provider adapter availability and whether the resolved adapter supports the requested route. | Use a supported provider, adapter, endpoint, or model. |
503 with all candidates unavailable. | Multi-target model health, cooldown state, and routing filters. | Restore a healthy target or adjust routing behavior. |
| Model health is degraded or down. | Recent upstream failure streak, provider outage, quota, outbound network path, and credential validity. | Restore provider reachability or route traffic to a healthy target. |
Compare the failing route with the matching provider upstream guide when the problem is provider-specific.
Read a Transport Error
A transport error means the request never completed at the HTTP layer, so there is no upstream status code to interpret. Gateway logs record the cause chain alongside the message, which is what distinguishes the possible faults:
| Cause in the log line | Meaning | Where to look |
|---|---|---|
dns error: failed to lookup address information | The provider hostname did not resolve. | DNS configuration, the api_base hostname, and egress DNS policy. |
tcp connect error: Connection refused | Nothing accepted the connection on that host and port. | The api_base port, and whether an intermediate proxy is listening. |
tcp connect error: Connection timed out | The connection attempt was black-holed. | Firewall, security group, and egress rules on the path. |
connection closed before message completed or a reset while sending | A pooled connection was closed by the far end before the request completed. | upstream.pool_idle_timeout_secs; see Tune the Upstream Connection Layer. |
| A TLS or certificate failure | The handshake with the provider or an intercepting proxy failed. | Trust roots, and whether a TLS-terminating proxy re-signs traffic. |
Errors that appear only intermittently against a provider that is otherwise healthy usually point to connection reuse rather than the provider. Lower pool_idle_timeout_secs below the shortest idle timeout on the path and re-check.
The same failure has a mirror image on the inbound side. If a gateway or load balancer in front of AISIX reports intermittent resets or 502s against a healthy AISIX, check whether downstream.idle_timeout_secs is set below that node's own pool idle timeout — the gateway would then be closing connections the node in front still considers usable. Leaving it at 0 (the default) rules this out. See Tune the Downstream Connection Layer.
Attribute an Upstream Error to the Right Hop
When the recorded message is upstream returned HTTP <status>: <body>, the status and body came from whatever answered at api_base — which is the provider only when nothing sits in front of it. Response bodies that mention a proxy's own vocabulary indicate an intermediate hop generated the error rather than the model provider:
upstream connect error or disconnect/reset before headerswith areset reason, orexceeded request buffer limit while retrying upstream, are emitted by Envoy-based proxies, service meshes, and API gateways.- A generic HTML error page is emitted by a reverse proxy or load balancer, not by a provider's JSON API.
On modeled routes, the gateway records these bodies for diagnosis but does not return them to the caller. Upstream 5xx responses reach the application as a generic 502 envelope. Passthrough routes instead forward the upstream provider's native status and body. Use the per-attempt records to identify the failing provider_key_id and target model, then compare them with access logs from the upstream endpoint configured in api_base.
Verify AISIX Enterprise Projection
Use this step when control-plane state and live gateway behavior do not match, or when an AISIX gateway cannot receive projected configuration.
| Signal | Check | Action |
|---|---|---|
| AISIX Enterprise heartbeat fails. | Certificate identity, trust roots, runtime state, control-plane URL, and outbound network path. | Restore AISIX Enterprise connectivity before investigating resource projection; see Connect an AISIX Gateway. |
The control plane itself fails to start, or cp-api logs a database role, schema, or permission error at startup. | The connection role's database privileges and the public/auth schema state, from the cp-api startup log. | If the control plane points at an external database you provisioned yourself, see the External Database Troubleshoot table for the exact error strings and fixes. If it runs the Helm chart's own bundled PostgreSQL sub-chart instead, that role does not own every schema the same way — see Bundled PostgreSQL (Helm) for its auth-schema permission model. |
| The control plane shows a resource but live traffic does not use it. | Environment scope and projection status for the gateway handling traffic. | Move the resource to the correct environment or wait for projection to complete. |
| AISIX Enterprise budget checks fail or appear unavailable. | Control-plane connectivity, budget policy target, and budget-check response details. | Restore budget-check connectivity or correct the AISIX Enterprise policy. |
| Playground succeeds but live traffic differs. | Live gateway, environment, model alias, caller key, and provider target. | Send the live request through the intended AISIX gateway and environment. |
Use the relevant feature guide or reference page after identifying the failing layer. Re-run the caller-facing request after each correction to verify the complete path rather than only the component that failed.