groups:
- name: langwatch-gateway
interval: 30s
rules:
# ─── Reliability ─────────────────────────────────────────────────────
- alert: GatewayHighErrorRate
expr: |
sum(rate(gateway_http_requests_total{status=~"5.."}[5m]))
/ sum(rate(gateway_http_requests_total[5m])) > 0.05
for: 5m
labels: { severity: page, team: ai-platform }
annotations:
summary: "Gateway 5xx rate > 5% over 5m"
description: |
End-user requests to the gateway are returning 5xx at {{ $value | humanizePercentage }}.
Check /readyz on pods, then upstream provider status.
- alert: GatewayReadinessFlapping
expr: |
changes(kube_pod_status_ready{
condition="true",
namespace="langwatch",
pod=~"langwatch-gateway-.*"
}[10m]) > 4
for: 5m
labels: { severity: warn, team: ai-platform }
annotations:
summary: "Gateway pod {{ $labels.pod }} readiness flapped > 4 times in 10m"
description: "Pod is flipping ready/not-ready. Investigate /readyz output."
# ─── Provider health ─────────────────────────────────────────────────
- alert: CircuitOpenTooLong
expr: gateway_circuit_state == 1
for: 5m
labels: { severity: page, team: ai-platform }
annotations:
summary: "Circuit open for credential {{ $labels.credential_id }} > 5m"
description: |
Gateway has tripped the breaker and is skipping this provider.
Verify the provider is actually having issues (check X-LangWatch-
Provider headers on recent failures + provider status page).
- alert: ExcessiveFallback
expr: |
sum(rate(gateway_provider_attempts_total{outcome="fallback_success"}[10m]))
/ sum(rate(gateway_provider_attempts_total[10m])) > 0.1
for: 10m
labels: { severity: warn, team: ai-platform }
annotations:
summary: "> 10% of requests falling back over 10m"
description: |
Primary provider is degraded enough that > 10% of requests are
walking the fallback chain. Not paging because fallback is
working as designed, but the primary needs investigation.
# ─── Budget governance ───────────────────────────────────────────────
- alert: StreamingUsageMissing
expr: |
sum(rate(gateway_streaming_usage_missing_total[10m])) > 0
for: 10m
labels: { severity: page, team: ai-platform }
annotations:
summary: "Streaming requests without usage reported"
description: |
A streaming request completing without token counts records $0 of
cost on the span, so the ledger never accumulates it and budgets
are silently bypassed. OpenAI requires stream_options.include_usage
=true on the client.
See /ai-gateway/streaming#usage-extraction-critical-for-streaming-budgets.
# The gateway does not debit budgets. Cost rides the OTel span and the
# control plane's trace-fold reactor writes the ClickHouse ledger, so
# there is no gateway-side queue to alert on. The signal that
# enforcement is actually firing is the block counter below.
- alert: BudgetBlockSpike
expr: |
sum by (scope) (rate(gateway_budget_blocks_total[10m])) > 1
for: 10m
labels: { severity: warn, team: ai-platform }
annotations:
summary: "Budget scope {{ $labels.scope }} is blocking > 1 req/s over 10m"
description: |
A hard-cap scope is rejecting sustained traffic. Either the limit
needs raising or a caller is looping. Precheck is local against the
cached bundle snapshot, so a scope that should be blocking but is
not means the bundle has not refreshed; check
gateway_control_plane_requests_total{endpoint="changes"}.
# ─── Auth cache ──────────────────────────────────────────────────────
- alert: AuthCacheHitRateDropped
expr: |
sum(rate(gateway_auth_cache_hits_total{tier="l1"}[5m]))
/ ( sum(rate(gateway_auth_cache_hits_total{tier="l1"}[5m]))
+ sum(rate(gateway_auth_cache_misses_total{tier="l1"}[5m])) ) < 0.9
for: 15m
labels: { severity: warn, team: ai-platform }
annotations:
summary: "L1 auth cache hit rate < 90% for 15m"
description: |
Sustained cache-miss rate means every miss costs a /resolve-key
round-trip to the control plane. Investigate: recent deploy churn,
/changes feed reliability, or LRU eviction under load.
# ─── Cost & anomaly ──────────────────────────────────────────────────
# Note: the registry carries no per-request cost metric, by design.
# Cost anomalies are easier to derive from the ledger table in the
# control-plane warehouse than from Prometheus, since cost attribution
# happens control-plane side when the trace-fold reactor folds the span.
# See /gateway/usage UI for the visual equivalent.
# ─── Blocked-by-policy noise ────────────────────────────────────────
- alert: GuardrailBlockSpike
expr: |
sum by (direction) (
rate(gateway_guardrail_verdicts_total{verdict="block"}[5m])
) > 10
for: 5m
labels: { severity: warn, team: ai-platform }
annotations:
summary: "> 10 req/s blocked by policy on direction={{ $labels.direction }}"
description: |
Legit block storm (a customer's runtime is trying a banned tool
repeatedly) or a regression in the policy_rules config.
Check LangWatch traces filtered on
attr.langwatch.policy.blocked != "".