🐛 Pantallas de error del sistema¶
Cuando HIX devuelve un error HTTP (404, 403, 405, 422, 429, 500, 503...)
a un cliente que pide HTML, sirve una pantalla de error. Si el
cliente pide JSON (API, AJAX, Accept: application/json), devuelve
{ "error": "...", "detail": "..." } automáticamente.
HIX permite sustituir las pantallas HTML por tus propios HTML estáticos
por cada código de estado: error_404.html, error_500.html,
error_403.html, etc.
🆚 Esta página cubre las pantallas del sistema (errores HTTP de rutas no encontradas, no permitidas, saturación, etc). Si lo que quieres es customizar la pantalla 5xx que se renderiza al fallar tu propio código (excepciones, traces de Harbour), eso vive en programacion/errorsys.
¿Cuándo lo necesitas?¶
- Para branding: que un 404 muestre tu logo, no la página gris genérica.
- Para redirigir a una landing personalizada (
/no-encontrado, página de mantenimiento, etc). - Para multiidioma del mensaje (404 traducido por locale).
- Para ocultar información técnica en producción (que no salga "HIX Web Server" al pie).
Setup en hix.ini¶
Sección [paths]¶
Ubicación fija: <root>/errors/¶
HIX busca páginas de error personalizadas en la carpeta fija errors/
relativa a paths.root (por defecto www/errors/):
www/
├── errors/
│ ├── error_404.html
│ ├── error_403.html
│ ├── error_500.html
│ └── error_503.html
└── ...
Si el fichero para un código concreto no existe, HIX usa la pantalla inline minimalista built-in.
Cómo funciona el lookup¶
Cuando HIX llama internamente a HIX_HttpError(oReq, nStatus, cDetail):
HIX_HttpError(oReq, 404, "Route: /no-existe")
│
▼
¿HIX_WantsJson(oReq)?
├── Sí → JSON {"error":"Not Found","detail":"..."}
│
└── No → HIX_HttpErrorHtml(404, "Not Found", "...")
│
▼
¿Existe <root>/errors/error_404.html?
├── Sí → hb_MemoRead() → sirve ese HTML
│
└── No → HTML inline minimalista de HIX
La negociación JSON vs HTML mira:
Accept: application/jsonoapplication/jsonen cualquier parte.X-Requested-With: XMLHttpRequest(AJAX clásico).Content-Type: application/jsondel request.
Códigos HTTP devueltos por HIX¶
HIX puede emitir cualquier código estándar. Los más comunes que ves en el día a día:
| Código | Cuándo | Detalle típico |
|---|---|---|
| 400 | Body o parámetro inválido | Bad Request |
| 401 | Falta auth (JWT/API key/sesión) | Unauthorized |
| 403 | Sin permisos para esa ruta o IP filtrada | Forbidden |
| 404 | Ruta no registrada o fichero no encontrado | Not Found, Route: /... |
| 405 | Ruta existe pero método no permitido | Method Not Allowed |
| 413 | Body más grande que [bodylimit] |
Payload Too Large |
| 422 | Validación falló (UValidateOrFail) |
Unprocessable Entity |
| 429 | Rate-limit superado | Too Many Requests |
| 500 | Excepción en tu código | Internal Server Error |
| 502 | Backend / API externa caída | Bad Gateway |
| 503 | Pool saturado, modo mantenimiento | Service Unavailable |
La lista completa la mantiene HIX_StatusText(nStatus) en
src/hix_error.prg.
Ejemplo de error_404.html¶
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Página no encontrada</title>
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body>
<header>
<img src="/static/img/logo.svg" alt="MiApp">
</header>
<main class="error">
<h1>404</h1>
<p>Esta página ha desaparecido.</p>
<a href="/">Volver al inicio</a>
</main>
<footer>
<small>© 2026 MiApp</small>
</footer>
</body>
</html>
📌 Los
error_XXX.htmlson HTML estático puro servido víahb_MemoRead(). No pasan por el motor de vistas. Si necesitas contenido dinámico (variables interpoladas), usa programacion/errorsys con un.view.html.
Pantalla inline por defecto¶
Cuando no hay error_XXX.html, HIX sirve algo como:
<!DOCTYPE html>
<html>
<head>
<title>404 Not Found</title>
<style>body{font-family:sans-serif;padding:2em;color:#333}h1{color:#c00}</style>
</head>
<body>
<h1>404 Not Found</h1>
<h2 style='color:#c00'><small>Route: /no-existe</small></h2>
<hr>
<small>HIX Web Server</small>
</body>
</html>
Compacto, autosuficiente y sin recursos externos. Para producción público se recomienda siempre sustituirlo.
Respuesta JSON automática¶
Si el cliente pide JSON, HIX emite directamente:
Para errores 422 desde UValidateOrFail, además incluye el detalle de
campos inválidos:
{
"error": "Unprocessable Entity",
"errors": {
"email": [ "El campo email es obligatorio" ],
"age": [ "El campo age debe ser numérico" ]
}
}
🤖 Para una API REST nunca necesitas tocar nada: el comportamiento por defecto ya es el correcto.
Generar errores desde tu código¶
Desde un controlador¶
USendError( 404, "Usuario no existe" )
USendError( 403, "Sin permisos para esta operación" )
USendError( 422, "Email obligatorio" )
USendError( 503, "Base de datos en mantenimiento" )
USendError se respeta la negociación: si el cliente pide JSON, devuelve
JSON; si pide HTML, devuelve la pantalla custom (o la inline si no la
hay).
Desde un middleware¶
FUNCTION HixMwApiKey( oCtx )
IF Empty( oCtx:oReq:Header( "X-Api-Key", "" ) )
oCtx:lHandled := .T.
HIX_HttpError( oCtx:oReq, 401, "API key requerida" )
RETURN .F.
ENDIF
RETURN .T.
HIX_HttpError es el helper de bajo nivel que también respeta JSON/HTML.
Diferencia con programacion/errorsys¶
| Caso | Qué se usa |
|---|---|
| Ruta no encontrada (404) | errors/error_404.html |
| Sin permisos (403) | errors/error_403.html |
| Validación falló (422) | errors/error_422.html + JSON detalle |
| Excepción en tu código (Harbour error) | errors/errorsys.view.html (errorsys) |
| Saturación de pool (503) | errors/error_503.html |
Es decir:
- Errores HTTP del flujo normal →
error_XXX.htmlestáticos. - Crash de tu código → template
.view.htmlcon datos del error (línea, fichero, traza). Eso es errorsys.
Puedes (y debes) usar ambos sistemas a la vez en una app de producción.
Errores típicos¶
| Síntoma | Causa | Fix |
|---|---|---|
Mi error_404.html no aparece |
Fichero fuera de www/errors/ |
Colocarlo en <paths.root>/errors/ |
| Aparece JSON en lugar del HTML | El cliente envía Accept: application/json |
Es correcto - no tocar |
| HTML sale sin CSS | Rutas relativas en el HTML | Usar /static/... absoluto |
| 500 sale con datos técnicos en producción | [behavior] env = dev |
Cambiar a env = prod |
| Mi template no interpola variables | Es HTML estático, no .view.html |
Usar errorsys |
Buenas prácticas¶
- Siempre define al menos
error_404.htmlyerror_500.htmlpara producción. Es la primera impresión de tu marca cuando algo va mal. - Mantén las páginas ligeras y autocontenidas: sin JS pesado, sin llamadas externas. Una página de error que tarda no debe encadenar más errores.
- No filtres detalles técnicos en producción: nada de stacktraces,
paths del servidor, ni nombres de tablas. Eso es para
devo el log. - Para multiidioma: usa negociación de
Accept-Languageen tu middleware de i18n y devuelveerrors/es/error_404.htmloerrors/en/error_404.htmlcon un router custom. - Si tu app es sólo API (sin HTML), no hace falta crear pantallas - la respuesta JSON automática es suficiente.