🧨 Manejo de errores¶
Todo lo que puede fallar (un acceso a BD, un parse JSON, una división por
cero, un fichero ausente) termina en un error de Harbour (un objeto
oError con description, subSystem, operation, ...). Sin un manejo
explícito, el worker que ejecuta la acción muere y el cliente recibe una
respuesta vacía o el servidor entero cae.
HIX expone dos niveles de defensa:
- TRY / CATCH local — dentro de una acción concreta, para fallos que sabes que pueden pasar (BD, network, parseo).
- Handler global (
bOnError) — red de seguridad que captura cualquier error no atrapado, envía un response HTTP coherente al cliente y escribe enerrors.log.
GET /api/users/42
│
▼
action _UserGet()
│
├─── TRY ──── error BD ──── CATCH ────────── USendError(500, ...)
│ │
└─── otros errores no controlados ▼
│ cliente recibe JSON
▼
worker protect ─── bOnError(oErr, oReq) ─── HIX_ShowError
│ │
▼ ▼
loggea + responde 500 errors.log + render
TRY / CATCH / FINALLY¶
Definidos en include/hix_const.ch:
TRY
// código que puede fallar
CATCH oError
// tratamiento del error
FINALLY
// se ejecuta siempre (con o sin error)
END
oError se declara siempre como LOCAL al inicio de la función:
FUNCTION _DbInsert( hData )
LOCAL oError, lOk := .F.
LOCAL oDbf
TRY
oDbf := UDbf():New( "customers" )
oDbf:Append()
oDbf:Save( hData )
lOk := .T.
CATCH oError
le( "DB insert failed: " + oError:description )
lOk := .F.
FINALLY
oDbf:Close()
END
RETURN lOk
Campos típicos de oError¶
| Campo | Contenido |
|---|---|
oError:description |
Mensaje principal del error |
oError:operation |
Función / operación que falló (OPEN, JSONDECODE, ...) |
oError:subSystem |
Subsistema (DBFCDX, BASE, MEMIO, ...) |
oError:subCode |
Código numérico — útil como status HTTP si está en rango |
oError:filename |
Fichero implicado |
oError:procName |
Función donde se disparó |
oError:procLine |
Línea de la función |
oError:args |
Argumentos pasados a la función fallida |
oError:cargo |
Hash libre — HIX lo usa para context extra (view code, line code, ...) |
Patrones en acciones¶
Validación + DB con error controlado¶
FUNCTION _UserCreate()
LOCAL oVal, oUsers, oError, nId := 0, cMsg := ""
oVal := UValidateOrFail( { ;
"name" => "required|string|max:50", ;
"email" => "required|string|email" ;
} )
IF oVal == NIL ; RETURN NIL ; ENDIF
TRY
oUsers := TUsers()
nId := oUsers:Insert( oVal:DataFields(), @cMsg )
CATCH oError
le( "Insert error: " + oError:description )
RETURN USendError( 500, oError:description )
END
IF nId == 0
RETURN USendError( 422, cMsg )
ENDIF
USendJson( { "id" => nId }, 201 )
RETURN NIL
Parse de JSON entrante¶
UJson ya devuelve NIL si falla — no necesitas TRY/CATCH explícito.
Acceso a fichero opcional¶
LOCAL oError, cContent := ""
TRY
cContent := hb_MemoRead( cPath )
CATCH oError
cContent := "(fichero no disponible)"
END
USendText( cContent )
Handler global (bOnError)¶
Cualquier error que no atrapes con TRY/CATCH cae aquí. El servidor
lo invoca con (oError, oReq):
oSrv:bOnError := {|oErr, oReq|
le( "Uncaught error: " + oErr:description )
HIX_HttpError( oReq, 500, oErr:description )
}
Si no defines bOnError, HIX usa su renderer interno (HIX_ShowError /
HIX_ErrorSys) que:
- dev: muestra HTML detallado con stack, línea fuente y context.
- prod: muestra HTML genérico 500 sin detalle interno.
Ver Errorsys para personalizar el template.
Diferenciar JSON / HTML automáticamente¶
oSrv:bOnError := {|oErr, oReq|
IF HIX_WantsJson( oReq )
oReq:Respond( { "error" => oErr:description }, 500, "json" )
ELSE
HIX_ShowError( oErr, oReq ) // delega al renderer interno
ENDIF
}
HIX_ShowErrorya hace este split por dentro: si elAcceptpide JSON, responde JSON; si pide HTML, renderiza el template errorsys.
Errores HTTP explícitos¶
No todo error es excepción. Muchos son situaciones esperadas:
USendError( 404, "Usuario no existe" )
USendError( 403, "Sin permiso" )
USendError( 422, "Datos inválidos" )
USendError( 429, "Demasiados intentos" )
USendError envía el status + cuerpo JSON o HTML según el Accept. Es
la forma de responder errores controlados desde la acción.
Equivalente directo¶
HIX_HttpError( oReq, nStatus, cMsg ) — recibe el oReq explícito,
útil dentro de middlewares.
Worker protect¶
Bajo el capó, cada worker HTTP envuelve la ejecución del controller en
HixWorkerProtect: si la acción suelta una excepción no atrapada, el
protect:
- Llama a
bOnErrorsi está definido. - Si no, llama a
HIX_ShowError. - Loggea la entrada en
errors.log. - Cierra la conexión limpiamente — no mata el worker, solo el request.
Es lo que evita que un error en una sola URL tumbe el servidor entero.
Logging del error¶
HIX_ShowError siempre llama a _HixWriteErrorLog antes de renderizar.
El fichero errors.log (configurado con HIX_ErrorLogInit o vía
[server] errors=.logs) acumula cada error con timestamp + secuencia.
Para logging libre fuera del flujo error usa los helpers de log:
ld( "Debug detail" ) // DEBUG
l( "Info" ) // INFO
lw( "Warning" ) // WARN
le( "Error description" ) // ERROR
Ver el módulo Logger.