Production QA reported "Submit failed 413" on /generation and plain
"Upload failed" (no detail) on resource upload. Root cause is the
outer reverse-proxy nginx on the VPS — still on default
`client_max_body_size 1m` — rejecting requests before they reach
Odoo. The inner docker-nginx and Odoo limits were already raised in
earlier commits; only the VPS proxy was left unpatched.
UI side
- Add `describeApiError(err, fallback, context)` helper in
lib/api-client.ts. Maps common HTTP codes to human-readable,
actionable toast descriptions (413 → "ask ops to raise nginx
client_max_body_size", 504 → "server took too long, retry", 502
→ "Odoo is restarting", 401 → "sign in again", 4xx/5xx → server
error body + status code).
- Use it in GenerationPage submit, ResourceManager upload,
TeacherLibrary upload, CustomExamCreate save/publish. Replaces
four slightly-different ad-hoc mappings with one source of truth.
Deploy side
- Add deploy/nginx-vps.conf.example: full TLS reverse-proxy template
with the body/timeout knobs that must match Odoo's limit_request
(128 MB) and limit_time_real (900 s).
- Add docs/DEPLOY_413_504_FIX.md: step-by-step remediation guide for
the team lead covering the 3 layers that can block large bodies
(outer nginx, Cloudflare, Odoo worker) and how to verify each.
Made-with: Cursor