Most issues with Unitru Academic fall into three categories: CAPTCHA or authentication failures during the automated login flow, SUV navigation or extraction errors caused by portal changes, and deployment or connectivity problems specific to the Docker or Railway environment. Use the sections below to identify and resolve the most common problems.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Andr21Da16/UNITRU-ACADEMIC/llms.txt
Use this file to discover all available pages before exploring further.
Domain exception reference
The backend maps every known failure mode to a user-facing error message sent over the WebSocket as anerror event. The table below lists each exception class, its location in the codebase, and the Spanish message the frontend displays.
| Exception class | User-facing message |
|---|---|
CaptchaError | No se pudo resolver el captcha. Intenta nuevamente. |
AuthenticationError | No se pudo iniciar sesión. Revisa tu usuario y contraseña. |
NavigationError | Hubo un problema navegando en el SUV. Intenta nuevamente. |
ExtractionError | No se pudieron extraer las notas. Intenta nuevamente. |
SuvTimeoutError | El SUV tardó demasiado en responder. Intenta más tarde. |
SuvUnavailableError | El SUV no está disponible en este momento. |
SuvError class defined in backend/src/domain/exceptions/suv_errors.py. An unrecognised exception type falls through to the generic message: “Ocurrió un error inesperado. Intenta nuevamente.”
Authentication and CAPTCHA
Login fails / CAPTCHA not solved
Login fails / CAPTCHA not solved
The backend uses Tesseract OCR to solve the SUV’s image CAPTCHA, with up to 3 automatic retries per WebSocket session. A
CaptchaError is raised only when all retries are exhausted.Steps to diagnose:- Try again — transient OCR failures are normal and a fresh session often succeeds.
-
If the error is consistent across many attempts, the SUV may have changed its CAPTCHA image format or the image element selector. Run
inspect_suv_login.pyto dump the current page structure: -
Check the
=== IMAGENES ===section of the output. The CAPTCHA image should appear withid="imgCaptcha"andsrc="/captcha.php". If the selector has changed, updateSuvAuthenticatorinbackend/src/infrastructure/playwright/suv_authenticator.pyto match.
Authentication error after entering correct credentials
Authentication error after entering correct credentials
An
AuthenticationError is raised when the SUV login form rejects the submitted credentials — even if they are correct. This can happen for several reasons unrelated to the app:- The SUV portal may be temporarily blocking sessions (rate limiting or IP-based blocks).
- The portal may be in a transient error state after a maintenance window.
- The student’s account may have a restriction set by the university.
- Wait a few minutes and try again.
- Try logging in manually at
https://suv2.unitru.edu.pe/to confirm credentials work. - If manual login succeeds but the app continues to fail, run
inspect_suv_login.pyto check whether the form field names have changed.
SUV availability
SUV is unavailable (SuvUnavailableError)
SUV is unavailable (SuvUnavailableError)
Timeout errors (SuvTimeoutError)
Timeout errors (SuvTimeoutError)
A
SuvTimeoutError is raised when a page load or navigation step within the SUV exceeds the configured Playwright timeout. The portal is known to respond slowly under heavy load, particularly at the start of a semester when many students access it simultaneously.Steps to resolve:- Try again later, especially outside peak hours (early morning or late evening).
- If timeouts are systematic, check whether the SUV portal itself is slow to respond in a normal browser.
- This is not a bug in the app — the Playwright timeout values are already generous.
Navigation and extraction
Navigation or extraction errors
Navigation or extraction errors
Schedule optimizer
Course missing from schedule optimizer results
Course missing from schedule optimizer results
If a course does not appear in the optimized schedule suggestions, or is listed in the
missing array, it means data/horarios_catalogo.json does not contain any section for that course in the current catalog.Steps to resolve:-
Run
test_optimizer.pywith the course name to see whether the catalog contains it and which names it does recognize. Themissinglist is printed to stdout. - Verify that the course name matches the exact spelling used in the official Google Sheets schedule (including accents and Roman numerals).
-
Re-download and re-parse the catalog with the latest spreadsheet URL:
-
Run
test_optimizer.pyto confirm the course now appears: -
Commit the updated
data/horarios_catalogo.jsonand redeploy the backend so the new catalog is baked into the Docker image.
WebSocket connectivity
Frontend can't connect to backend (WebSocket error or blank dashboard)
Frontend can't connect to backend (WebSocket error or blank dashboard)
The frontend opens a WebSocket connection to the URL embedded in
NEXT_PUBLIC_BACKEND_WS_URL at page load. If this URL is wrong or the backend is unreachable, the dashboard will never receive data.Steps to diagnose:- Open the browser developer tools → Network tab → filter by “WS”. Check that a WebSocket connection is being attempted to the correct URL.
-
Verify that
NEXT_PUBLIC_BACKEND_WS_URLpoints to the correct backend address:- Local:
ws://localhost:8000/ws - Railway production:
wss://your-backend-domain.up.railway.app/ws
- Local:
-
In Railway, ensure the variable uses the
wss://scheme (TLS). Usingws://over a Railway HTTPS domain will fail because Railway’s proxy enforces TLS. -
Confirm the backend
/healthendpoint is reachable: -
Verify that
ALLOWED_ORIGINSon the backend includes the exact frontend origin (scheme + domain, no trailing slash). A CORS rejection will prevent the WebSocket upgrade.
Railway deployment
Frontend shows blank page or 502 after deploy
Frontend shows blank page or 502 after deploy
This almost always means
NEXT_PUBLIC_BACKEND_WS_URL was not available during the Docker build, so the Next.js bundle was compiled with the default ws://localhost:8000/ws instead of the production URL.Steps to resolve:-
Confirm the variable is set in the Railway Variables tab of the frontend service — not in a
.envfile or post-build setting. -
Check that it is wired as a build argument (Railway passes
NEXT_PUBLIC_*variables automatically as Docker build args when set in the Variables tab). -
If the variable was added or changed after the last build, trigger a manual Redeploy in Railway to force a fresh
docker buildwith the correct value. -
Check the Railway build logs for a line like:
If you see
ws://localhost:8000/wsthere instead, the variable was not picked up.
First Docker build is very slow
First Docker build is very slow
This is expected behavior. The backend
Dockerfile installs Tesseract OCR via apt-get and then runs playwright install --with-deps chromium, which downloads the Chromium browser binary and all of its system library dependencies. Together these downloads total approximately 500 MB.Subsequent builds are fast because Docker caches the layers for the system packages and the Playwright browser installation. The cache is invalidated only when requirements.txt changes or when the base image is updated.There is nothing to fix — wait for the first build to complete.Grades display
Grades show None instead of numbers
Grades show None instead of numbers
A grade field showing
None in the dashboard means the professor has not yet entered that grade in the SUV. The backend uses Python Decimal for all grade values and explicitly represents unpublished grades as None — never as 0 or an empty string — to distinguish “not yet graded” from “graded zero”.This is intentional behavior, not a bug. No action is needed on the app side. The grade will appear once the professor publishes it in the SUV.