Troubleshooting
Common issues when developing and deploying federated Shell apps.
Audience: Shell and federated app developers
Symptoms grouped by layer. Start with bootstrap (public, no auth), then auth, then pillar APIs.
Shell bootstrap
Bootstrap returns 404
| Cause | Fix |
|---|---|
Wrong slug | Match Portal app detail slug exactly |
Wrong workspace_id | Copy UUID from Portal workspace URL |
| App archived | Re-register or un-archive in Portal |
| Typo in URL | Path is /v1/public/workspaces/{uuid}/apps/by-slug?slug= |
Bootstrap returns 400
App exists but app_kind is not shell_federated. Register a new Shell app or use Coderunner docs for other kinds.
manifest.remoteUrl is empty
| Cause | Fix |
|---|---|
| Never published | Portal → app → Publish build |
| Publish failed | Check finalize step; confirm MinIO/object store up |
| Dev without publish | Set SHELL_REMOTE_DEV_URL on Shell host |
remoteEntry.js returns 404 or CORS error
| Cause | Fix |
|---|---|
| Stale URL after re-publish | Re-fetch bootstrap (cache TTL) |
| Object store down | docker compose -f deploy/docker-compose.phase2-deps.yml --profile minio up -d |
| Wrong public base URL | Check OBJECT_STORE_PUBLIC_BASE_URL in Control Plane .env |
| Browser blocked | MinIO CORS for Shell origin |
Module Federation load failure
| Cause | Fix |
|---|---|
appId mismatch | Remote scope must equal manifest.appId |
| Wrong remote URL format | URL must point at remoteEntry.js file |
| Shared dependency version skew | Align React/webpack versions with Shell host |
Authentication (Keycloak)
401 Invalid token / issuer mismatch
| Cause | Fix |
|---|---|
KEYCLOAK_ISSUER wrong on API | Match token iss claim (no trailing slash) |
| Expired token | Call keycloak.updateToken(30) before API calls |
| Wrong realm/client | Align NEXT_PUBLIC_KEYCLOAK_* with Portal |
401 Token audience mismatch
API KEYCLOAK_AUDIENCE must include your browser client's id. Token often has aud: "account" — check azp claim and add that client id to the comma-separated list.
403 Forbidden
User is authenticated but lacks SpiceDB permission on the workspace.
| Need | Permission |
|---|---|
| Read vault/files/secrets/models | read |
| Upload files, write vault | write |
| Mint LLM virtual key | administer |
| Create/drop collections | ddl |
Add membership in Portal → workspace → members.
CORS error from browser
Add Shell origin to Control Plane CORS_ORIGINS in .env:
["http://localhost:3000","http://localhost:3001"]Restart API after change.
Data Vault
503 No Dataplane service API key
Workspace provisioning did not store DATAPLANE_SERVICE_API_KEY in Infisical.
- Confirm workspace status is
active - Re-run provisioning or
scripts/backfill_dataplane_workspace_key.py - Bring up Infisical — Phase 2 deps runbook
502 Dataplane auth failed (401)
| Cause | Fix |
|---|---|
| Invalid workspace key | Re-provision workspace |
| Dataplane not running | Start groundfloor-dataplane-oss stack |
Wrong DATAPLANE_URL | Default http://localhost:8080 |
502 malformed response / timeout
Dataplane reachable but collection/engine error. Check Dataplane logs; verify collection name in query path.
404 Collection not found
Collection not created in this workspace tenant. Use Portal Data Vault UI or DDL API — 04-data-vault.md.
Empty query results
Filter too restrictive; wrong collection; or no data. Try query without filter first.
Files
409 on finalize — bucket object missing
PUT to presigned URL failed or was skipped.
- Confirm PUT returned 200
- Check URL not expired (1h default)
- Verify
Content-Typeheader matches init request
409 on download — file not active
Still pending (finalize not called) or deleted.
Browser upload fails silently
CORS on MinIO; ad blocker; or mixed content (HTTPS Shell → HTTP MinIO in dev).
Secrets
502 Bad gateway
Infisical not running or machine identity not configured in Control Plane .env.
404 on reveal
Key does not exist — list keys first with GET …/secrets.
LLM Gateway
502 on mint virtual key
LiteLLM not running:
docker compose -f deploy/docker-compose.phase2-deps.yml --profile litellm up -d
curl http://localhost:4000/health/livenessLiteLLM chat completion fails
| Cause | Fix |
|---|---|
| No provider keys | Add OPENAI_API_KEY (or similar) to workspace Secrets |
| Invalid virtual key | Re-mint and update LITELLM_API_KEY secret |
| Wrong model id | Use gf-chat-default etc. from GET …/llm/models |
Publish (app releases)
Zip publish — remote not found
remoteEntry.js must be at zip root, not nested in a folder.
App status stuck registered
Normal until first successful publish → transitions to active.
Debugging commands
# API health
curl -s http://localhost:8088/health | jq .
# Bootstrap
curl -s "http://localhost:8088/v1/public/workspaces/${WORKSPACE_ID}/apps/by-slug?slug=${SLUG}" | jq .
# Remote entry HEAD
curl -sI "$(curl -s ... | jq -r .manifest.remoteUrl)" | head -5
# Vault collections (needs TOKEN)
curl -s "http://localhost:8088/v1/workspaces/${WORKSPACE_ID}/vault/collections" \
-H "Authorization: Bearer ${TOKEN}" | jq .More recipes: 11-local-dev-recipes.md.
Get help
| Resource | Location |
|---|---|
| Portal bootstrap URLs | App detail → Shell bootstrap card |
| OpenAPI try-it | {CONTROLPLANE_URL}/docs |
| Phase 1 plan | docs/plans/federated-shell-apps-phase1.md |
| Control Plane pillar status | PLAN.md |