Troubleshooting: Auth and credentials
"PKCE state mismatch — login flow is invalid or expired"#
The browser stored a PKCE verifier in sessionStorage under one tab, the ScaiKey redirect landed in another tab (or after a refresh that lost session storage).
Fix: Sign in again. If it keeps happening, ensure the canvas is served from a single origin (no localhost:5173 vs 127.0.0.1:5173 mismatch) and that redirect_uri exactly matches the one configured in ScaiKey.
503 ScaiKey OIDC not configured on the server#
The backend is missing one of SCAIKEY_BASE_URL, SCAIFLOW_SCAIKEY_CLIENT_ID, SCAIFLOW_SCAIKEY_CLIENT_SECRET.
Fix: Set all three in the backend's .env and restart. See Configuration.
400 invalid_grant: code already used#
The auth code returned from ScaiKey can only be exchanged once. The canvas probably triggered the exchange twice (double-click on the callback, dev hot-reload re-running the effect, etc.).
Fix: Refresh, sign in again. If it persists in dev, check whether your hot-reload setup re-mounts <AuthCallback> — it should consume the code on mount and clear the URL.
401 invalid token: Audience doesn't match#
ScaiGrid (or ScaiFlow's own JWT verifier) rejected the token because the aud claim doesn't match the configured audience.
Fix: ScaiKey issues aud = client_id by default. Make sure SCAIKEY_AUDIENCE on the backend is either unset (defaults to SCAIFLOW_SCAIKEY_CLIENT_ID) or explicitly equals the client_id ScaiKey is configured to use.
If you've set SCAIKEY_AUDIENCE to something other than client_id (e.g. user-portal), unset it — ScaiKey doesn't actually issue that audience.
"Save requires ScaiKey login" but I am signed in#
The canvas distinguishes two auth modes — scaikey (full backend access) and dev_token (legacy shared-bearer; only POST /v1/dev/deploy works). Save needs scaikey.
Fix: Ensure your backend has SCAIKEY_BASE_URL + client_id + secret configured. If they're set but the canvas is still in dev_token mode, check GET /v1/auth/config — it returns auth_mode: "scaikey" only when all three are present.
412 missing_credentials on Lookup endpoints#
Any /v1/scaiqueue/*, /v1/scaibunker/*, /v1/scaigrid/* proxy returning 412 means the backend has no ScaiGrid credentials for your tenant.
Fix: A tenant admin sets the tenant's API key via POST /v1/tenants/me/scaigrid_credentials. See Deploy failures: 412 missing_scaigrid_credentials.
"Catalog requires ScaiKey login" on the Catalog button#
Same root cause — Catalog endpoints are ScaiKey-only. Sign in via ScaiKey or wait for ScaiKey to be configured.
You can still open the Catalog modal in dev-token mode — the Examples tab works (loads from static files); the Browse + Publish tabs are hidden.
ScaiKey-sync: 403 Platform token requires admin:read or admin:write scope#
The credentials the backend is using don't have the scopes needed to call ScaiKey's admin API (applications.get_effective_users, groups.list, etc.).
Fix: Re-issue the backend's ScaiKey credentials with admin:read and admin:write (or equivalent on your ScaiKey version). The scaiflow scaikey-register CLI does this in one shot for fresh tenants.
If your credentials are tenant-tier (no admin scope), the sync falls back to the member-walk path automatically — slower but works without admin scope. Look for users_method: "member_walk" in the sync stats to confirm the fallback kicked in.