---
title: 'Troubleshooting: Deploy failures'
path: troubleshooting/deploy-failures
status: published
---

# Troubleshooting: Deploy failures

The deploy endpoint returns a structured `detail.error` for every failure. Match yours below.

## `412 missing_scaigrid_credentials`

```jsonc
{ "detail": { "error": "missing_scaigrid_credentials",
              "message": "no ScaiGrid credentials for tenant 'acme'",
              "fix": "Sign in with ScaiKey, or set this tenant's API key via POST /v1/tenants/me/scaigrid_credentials." } }
```

The backend couldn't find any usable ScaiGrid credentials.

**Fix:** A tenant admin sets the tenant's API key via:

```bash
curl -X POST "https://scaiflow.example/api/v1/tenants/me/scaigrid_credentials" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{ "api_key": "sgk_xxxxxxxxxx", "base_url": "https://scaigrid.scailabs.ai" }'
```

Or, for dev: set `SCAIGRID_API_KEY` env var on the backend process (only honored when no tenant key is set).

## `502 scaiqueue_auth`

```jsonc
{ "detail": { "error": "scaiqueue_auth",
              "message": "permission_denied: need scaiqueue:manage",
              "fix": "Caller needs scaiqueue:manage to provision missing scopes/queues." } }
```

The pre-deploy ScaiQueue provisioning step (which runs when `flow.config.scaiqueue` is set) couldn't authenticate to ScaiQueue.

**Fix:** Grant `scaiqueue:view` + `scaiqueue:manage` to the caller's principal (via ScaiKey). For tenant-tier credentials, ensure the tenant's API key has those scopes too.

If you don't need pre-deploy provisioning (e.g. the scope already exists), just remove `flow.config.scaiqueue` and ScaiFlow will skip the step. You'll still need to reference scopes/queues by slug in node configs, but the deploy won't fail.

## `502 scaiqueue_provision_failed`

```jsonc
{ "detail": { "error": "scaiqueue_provision_failed",
              "message": "...",
              "scope": "support", "queue": "review" } }
```

Provisioning failed for non-auth reasons (slug conflict, network error, etc.). The `message` is the upstream error.

**Fix:** Check whether the scope already exists with a different config than what your flow declares. ScaiQueue's POST on slug conflict raises 409 (no return-existing semantics); `ensure_*` helpers re-list on 409, but if the second list also fails or shows a config mismatch, deploy aborts.

## `502 scaicore_create_failed`

```jsonc
{ "detail": { "error": "scaicore_create_failed",
              "message": "verifier error: ...",
              "status": 400 } }
```

ScaiGrid rejected the manifest. Usually a verifier error.

**Fix:** Compile locally with verification on and read the diagnostic:

```bash
scaiflow compile my-flow.flow.json --format ir
```

The verifier catches IR shape mismatches that the YAML loader silently tolerates. Common causes:

- Plugin call missing a required arg.
- `llm_role` referenced but not declared (the canvas + compiler catch this before deploy now, but older flows might slip through).
- Block kind not in ScaiCore's closed set (shouldn't happen — file a bug if it does).

## `502 scaicore_publish_failed`

```jsonc
{ "detail": { "error": "scaicore_publish_failed", "core_id": "core_xxx" } }
```

The Core was created successfully but the `/publish` step failed.

**The Core is still deployed and invokable.** It just isn't registered as a chat model. To retry the publish manually:

```bash
curl -X POST "https://scaigrid.scailabs.ai/v1/modules/scaicore/cores/${CORE_ID}/publish" \
  -H "Authorization: Bearer ${TOKEN}" \
  -d '{}'
```

Or re-deploy via the canvas (idempotent — updates the existing Core, re-tries the publish).

## Changing the publish state on an already-deployed Core

Setting `publish_as_model: false` and re-deploying does NOT un-publish — that requires:

```bash
curl -X DELETE "https://scaigrid.scailabs.ai/v1/modules/scaicore/cores/${CORE_ID}/publish" \
  -H "Authorization: Bearer ${TOKEN}"
```

A future ScaiFlow release will detect the transition and call this automatically.

## Deploy succeeds but no events show up

The Live Runs panel polls `GET /v1/scaicore/events`. Check:

- ScaiGrid is actually emitting events (call `GET /v1/modules/scaicore/events?core_id=$CORE_ID` directly).
- Your tenant has `scaicore:view` on the calling principal.
- The flow's `core_id` is registered in `/v1/flows/{id}/deployments` (the canvas reads this to know which Cores to poll for events for this flow).
- `auth_mode` is `scaikey` — events polling is gated on it.
