Fennec is a thin SPA — most “fennec errors” are upstream failures from brain
or the embedded Strapi backend, surfaced via TanStack Query error states.
This page enumerates the failures an operator is likely to see in the UI and
what to do about them.
Errors fall into five buckets:
- Auth — Clerk session, role gating, JWT issues
- Network — CORS, DNS, upstream unavailable
- Validation — 4xx from upstream, malformed payloads
- Build / runtime config — missing env vars, prefix mistakes
- Generations Dashboard — Media ID lookup results
| # |
What the user sees |
Likely cause |
First action |
| 1 |
Stuck on “Loading…” indefinitely after login |
useCurrentUserQuery failing silently — brain returns 401 but Clerk session loaded |
Check browser console for /users/me 401; verify REACT_APP_BRAIN_URL and that Clerk JWT is being sent |
| 2 |
Redirected back to /profile from a guarded route |
PrivateRoute saw isError or insufficient role from /users/me (PrivateRoute.tsx) |
Confirm the user’s role in brain DB; check useCurrentUserRole output in React DevTools |
| 3 |
Redirected to /login immediately on every page load |
Clerk session expired or not initialised; window.Clerk undefined |
Force sign-out, clear cookies for the Clerk frontend domain, sign back in |
| 4 |
“401 Unauthorized” toast on a brain mutation |
JWT expired mid-session; getClerkToken returned empty |
Reload page (Clerk refresh) or sign out/in. If persistent, see fennec-runbook.md Clerk section |
| 5 |
Login page renders but Clerk widget never mounts (blank panel) |
REACT_APP_CLERK_PUBLISHABLE_KEY missing or wrong-environment key |
Verify env value matches the Clerk instance for this deploy (dev vs prod) |
| 6 |
“Forbidden” / 403 on admin pages for an existing admin |
Role drift between Clerk metadata and brain users table |
Cross-check GET /users/me response; reconcile in brain |
| # |
What the user sees |
Likely cause |
First action |
| 7 |
“Network Error” or ERR_NETWORK on every brain call |
REACT_APP_BRAIN_URL wrong, brain down, or DNS failure |
curl $REACT_APP_BRAIN_URL/health; check Railway brain deploy status |
| 8 |
CORS preflight failure (“blocked by CORS policy”) |
Brain or Strapi CORS allowlist missing the fennec origin |
Update upstream CORS config; brain config in apps/brain/src/config/ |
| 9 |
“Cookies not sent” symptom: works locally, fails in deployed env |
withCredentials: true requires Access-Control-Allow-Credentials: true and explicit origin |
Confirm upstream is not using a * Access-Control-Allow-Origin; explicit origin required |
| 10 |
Spinner forever on a list page (e.g. Generations Dashboard) |
Upstream timeout; TanStack Query retrying silently |
DevTools → Network tab → look for hanging requests; check brain logs |
| 11 |
“502 / 503 Bad Gateway” toast or red page |
Upstream rolling deploy or crashed |
Check Railway deploy events for the affected upstream |
| # |
What the user sees |
Likely cause |
First action |
| 12 |
“400 Bad Request” on a save |
Form payload mismatch with upstream DTO; mapper drift |
Inspect request body in DevTools; compare with brain DTO definition |
| 13 |
“404 Not Found” on a known resource |
ID typo, soft-deleted record, or env points at the wrong DB |
Confirm REACT_APP_BACKEND_URL / REACT_APP_BRAIN_URL match expected environment |
| 14 |
“409 Conflict” creating a preset / prompt |
Duplicate name; unique constraint |
Rename and retry |
| 15 |
“413 Payload Too Large” uploading media |
Upstream body limit hit before R2; image not routed via pre-signed URL |
Use the pre-signed-URL flow (brainInputMedia.ts) for large files |
| 16 |
“415 Unsupported Media Type” |
Browser failed to set multipart boundary, or Content-Type overridden by caller |
Pass raw FormData to apiClient — it intentionally omits Content-Type for FormData |
| 17 |
“422 Unprocessable Entity” with no clear field error |
Strapi validation; missing relation |
Inspect Strapi response body — Strapi nests errors under error.details |
| # |
What the user sees |
Likely cause |
First action |
| 18 |
“undefined/users/me” appears as request URL in Network tab |
apiConfig.brain is undefined — REACT_APP_BRAIN_URL not baked into bundle |
Set the variable and rebuild (Vite inlines env at build time, not runtime) |
| 19 |
New env var added but value is undefined in app |
Missing REACT_APP_ prefix (see fennec-env.md) |
Rename variable; rebuild |
| 20 |
Image URLs broken on prod, fine in dev |
VITE_IMAGES_CDN_URL read in lib/utils.ts:9 ignored due to envPrefix: "REACT_APP_" |
Switch the read to REACT_APP_IMAGES_CDN_URL (or drop the override), set in Railway, redeploy. TODO(@law): pick rename vs remove. |
| 21 |
“Maximum update depth exceeded” on a page using Semantic UI form |
Legacy Semantic UI React component re-rendering in StrictMode-equivalent path |
Migrate component to shadcn/Base UI per apps/fennec/AGENTS.md guidance |
| 22 |
Blank page after deploy, console shows MIME-type or 404 on /assets/* |
CDN cache pinned to old index.html referencing dropped chunk hashes |
Flush CDN per fennec-runbook.md; re-rollout |
The Media ID filter reads one row from Brain; these rows separate “not in Brain” from “lookup failed”.
| # |
What the user sees |
Likely cause |
First action |
| 23 |
Generations Dashboard Media ID filter shows “No generations found” for a known media |
Brain has no Generation row for it — the filter reads only Brain genId. Causes: not dispatched yet; failed at dispatch on a permanent input error or Brain 4xx (sendmediageneration.go); or failed once its generation window expired (checkmediageneration.go) |
Check the media status in Sirloin: FAILED means one of the failure paths; PENDING means queued — the dispatch worker retries it and it has no deadline; GENERATING (claimed before the Brain call, storage/media.go) expires after the generation window (10 min image, 45 min video, checkmediageneration.go) |
| 24 |
Generations Dashboard shows “Could not look up this media ID. Try again.” |
Brain detail lookup failed with a non-404 (5xx, timeout, 401) after retries |
DevTools → Network → GET /generation/dashboard/:id status; then row 4 (401) or row 11 (5xx) |
flowchart TD
A[User reports error] --> B{Auth-related?}
B -- yes --> C[Check Clerk session + /users/me + role]
B -- no --> D{Network tab shows 4xx?}
D -- 401/403 --> C
D -- 4xx other --> E[Validation: inspect payload + upstream DTO]
D -- 5xx --> F[Upstream incident: check Railway / brain logs]
D -- no request --> G[CORS or DNS — check console]
G --> H{undefined in URL?}
H -- yes --> I[Env var missing; rebuild required]
H -- no --> F