Cloud troubleshooting
Symptoms-first reference for operators running iterion in cloud mode (Helm chart, docker-compose, or any setup using Mongo + NATS + S3-compatible blob storage). For each symptom: how to diagnose, what to check, what fixes it.
For deployment / install instructions see cloud-deployment.md. For exposing iterion publicly see cloud-public-exposure-checklist.md.
Quick triage
# Server-side health
curl -fsS http://<server-host>:4891/healthz
curl -fsS http://<server-host>:4891/readyz
curl -fsS http://<server-host>:4891/metrics | head -20
# The server Deployment is named after the Helm release (`deploy/iterion`
# for a release named `iterion`); target it by that name or by the
# `-l app.kubernetes.io/component=server` label. The runner is
# `<release>-runner` (`-l app.kubernetes.io/component=runner`).
# Runner pool status (Kubernetes)
kubectl -n <ns> get pods -l app.kubernetes.io/component=runner
kubectl -n <ns> logs -l app.kubernetes.io/component=runner --tail=200
# Queue depth (NATS)
nats stream info ITERION_RUNS
nats consumer info ITERION_RUNS iterion-runners
# Mongo connectivity from server pod
kubectl -n <ns> exec deploy/iterion -- nc -zv <mongo-host> 27017
# Blob bucket connectivity from server pod
kubectl -n <ns> exec deploy/iterion -- aws --endpoint-url $S3_ENDPOINT s3 ls s3://$S3_BUCKET/runs/If /readyz returns 503: the server can reach itself but cannot reach Mongo, NATS, or blob storage. The body lists which probe failed.
Symptoms → diagnosis → fix
Runs queue but never start (status: queued for minutes)
Probable cause: no runner pod is consuming the NATS queue, OR runner is consuming but cannot acquire the lock, OR runner is consuming but cannot reach Mongo / blob.
Diagnose:
kubectl get pods -l app.kubernetes.io/component=runner— replicas > 0?nats consumer info ITERION_RUNS iterion-runners—Num Pendingdecreasing?Num Outstanding Acksnon-zero?kubectl logs -l app.kubernetes.io/component=runner --tail=200 | grep -E 'lock|claim|mongo|blob'
Fix:
- KEDA scaled to 0 with no runs queued is normal. Submit a run; KEDA should scale up within ~30s. If it doesn't: check
kubectl describe scaledobject iterion-runnerfor KEDA controller errors. - Lock contention (multiple runners racing on the same run): the loser will see
ErrLockHeldin logs and Nak the message. Expected — JetStream redelivers. If all runners loop onErrLockHeld, the run was leased and orphaned; wait for the 60s TTL ornats kv del iterion-run-locks <run-id>to force release. - Mongo / blob unreachable: NetworkPolicy or firewall. See networkpolicy-egress example.
Runs hang in running past their max_duration
Probable cause: runner pod was terminated mid-run (OOM, eviction, node drain), the lease expired, but no other runner picked it up; OR the engine lost its sandbox container without aborting.
Diagnose:
iterion inspect --run-id <id> --events | tail -50(orkubectl logs … | grep <id>) — last event before hang?nats kv get iterion-run-locks <run-id>— is the lease still claimed?kubectl get events -n <ns> --sort-by='.lastTimestamp' | tail -30— pod evictions, OOM kills?
Fix:
- If the lease is stale (
status: runningin lease but no runner pod alive): release withnats kv del iterion-run-locks <run-id>and the next runner will pick the run up via JetStream redelivery. The engine resumes from the last checkpoint. - If the run was OOM-killed: increase
runner.resources.limits.memoryin your values overlay; some workflows (especiallyclaude_codewith long context) need ≥ 2 GiB. - If the sandbox container is orphaned:
docker ps --filter ancestor=ghcr.io/socialgouv/iterion-sandbox-slimfrom the runner host shows lingering containers. Restart the runner pod; the engine drains and recreates sandboxes per run.
/readyz 503 with mongo: connection refused
Probable cause: server cannot reach Mongo at the configured ITERION_MONGO_URI.
Diagnose:
kubectl exec deploy/iterion -- env | grep MONGOkubectl exec deploy/iterion -- nc -zv <mongo-host> 27017kubectl get networkpolicy -n <ns>— does the egress allow port 27017 to the Mongo namespace?
Fix:
- Wrong URI: update the secret backing
ITERION_MONGO_URIand roll the deployment. - NetworkPolicy: see networkpolicy-egress example. The chart's default egress allows DNS only; cluster traffic is not implicit.
- TLS mismatch: cloud Mongo (Atlas, etc.) often requires TLS — set
ITERION_MONGO_URI=mongodb+srv://...?tls=true&retryWrites=true.
/readyz 503 with s3: AccessDenied
Probable cause: S3 credentials are wrong, the bucket doesn't exist, or the bucket policy denies the iterion server.
Diagnose:
kubectl exec deploy/iterion -- env | grep -E 'S3|AWS'- From inside the pod:
aws --endpoint-url $S3_ENDPOINT s3 ls s3://$S3_BUCKET/ - Check the bucket policy / IAM role for the access key.
Fix:
- Rotate the access key + secret in the secret backing
ITERION_S3_ACCESS_KEY_ID/ITERION_S3_SECRET_ACCESS_KEYand roll the deployment. - For MinIO: ensure the access key has
s3:GetObject,s3:PutObject,s3:DeleteObject,s3:ListBucketon the configured bucket. - For AWS: prefer IAM Roles for Service Accounts (IRSA) over static keys — set
serviceAccount.annotations.eks.amazonaws.com/role-arnin your values overlay and unset the*_ACCESS_KEY_IDenv vars.
Editor frontend connects but no events stream in
Probable cause: the WebSocket endpoint cannot reach MongoSource (when in cloud mode with NATS-driven runs), OR the JWT used by the studio lacks the right tenant scope, OR a proxy strips WS upgrade headers.
Diagnose:
- Browser devtools → Network → filter on
Upgrade: websocket. Does the handshake return 101? kubectl logs deploy/iterion | grep -E 'eventstream|ws|tenant'iterion inspect --run-id <id> --eventsfrom a TTY against the same store: do events exist?
Fix:
- 101 handshake fails behind a proxy: configure the Ingress to pass WebSocket Upgrade. Example for nginx ingress:
nginx.ingress.kubernetes.io/proxy-set-header: "Upgrade $http_upgrade". - Events exist but the stream is empty: the studio is filtering by tenant mismatch. Re-login to refresh the JWT.
- MongoSource unwired: confirm
ITERION_MODE=cloudis set on the server. Without it,runview.servicedefaults to the filesystem event source (runstream.FileSource) which won't see Mongo events.
Runs fail with budget_exceeded immediately
Probable cause: the workflow's max_cost_usd / max_tokens is below the cost of the first node call.
Diagnose:
iterion inspect --run-id <id> --events | grep -E 'budget|cost'- Inspect the
.botsource'sbudget:block.
Fix:
- Raise the cap and resume. Either edit the workflow source's
budget:block (max_cost_usd/max_tokens), or — with no source edit — pass a per-run override on resume:iterion resume --run-id <id> --max-cost-usd <n>(the same--max-cost-usd/--max-tokens/--max-duration/--max-iterations/--max-parallel-branchesflags accepted byiterion run). - For long-running review-fix loops, raise
max_iterationstoo — a low cap forces premature termination.
iterion bench asymptote shows all runs at iteration 0
Probable cause: the --judge-node flag does not match a node ID actually present in the workflow, OR no EventEdgeSelected events are emitted (no loop-back edges in the workflow).
Diagnose:
iterion inspect --run-id <id> --events | grep -E 'edge_selected|node_finished' | head -20iterion validate <workflow.bot>— confirm node IDs match what--judge-nodeexpects.
Fix:
- Pass the right node ID. The IR node ID is the value after
judge/agentin the.botsource (e.g.judge reviewer:→--judge-node reviewer). - If your workflow has no bounded loop (no
-> as loop_name(N)edges), the bench has nothing to iterate over. Add a loop or measure a different recipe.
Trivy CI reports a HIGH CVE
Probable cause: a dependency (Go module, npm package, or base image layer) has a newly-published HIGH-severity advisory. The Trivy workflow publishes SARIF and summaries; it does not fail PRs by itself unless your repository adds a separate hard gate through code scanning or branch protection.
Diagnose:
- Read the SARIF output uploaded to the PR's "Code scanning" tab.
trivy fs --severity HIGH,CRITICAL .locally to reproduce.
Fix:
- Update the offending dependency. Most Go advisories resolve by
go get -u <module>@<version>thengo mod tidy. - Container base image CVEs: rebuild from a fresh
iterion-sandbox-slimtag. The release pipeline emits a new tag every Monday. - Genuinely irrelevant CVE (e.g. a vulnerability only triggered by a code path iterion doesn't use): add a
.trivyignoreentry with a justification comment. Don't bypass without one — drift is how compliance findings accumulate.
Helm chart upgrade fails with manifests version drift
Probable cause: the CI guard saw a mismatch between charts/iterion/Chart.yaml appVersion and package.json version.
Diagnose:
git show HEAD -- charts/iterion/Chart.yaml package.json
Fix:
- Run
task chart:sync-version(bumps Chart.yaml to match package.json) and recommit.
What's not here
- Application-level workflow debugging — see resume.md and workflow_authoring_pitfalls.md.
- Sandbox container debugging — see sandbox.md.
- DSL / IR errors — see references/diagnostics.md.
- Editor UI bugs — file a GitHub issue.
Escalation
If a symptom isn't covered above and /readyz looks healthy:
- Capture the full event stream:
iterion report --run-id <id> --output report.md. - Capture server + runner logs:
kubectl logs ... > /tmp/iterion-logs.txt. - Open an issue at https://github.com/SocialGouv/iterion/issues with both attached. Redact API keys before posting (the privacy_filter / privacy_unfilter tools can help — see privacy_filter.md).
