Skip to main content

Overview

The Solicitud Transporte API implements comprehensive error handling with standardized response formats, detailed validation messages in Spanish, and multiple layers of error interception.
All error messages are localized to Spanish to match the target user base in El Salvador.

Response Format

All API responses follow a consistent structure defined in utils/response_handler.py:

Success Response

{
  "exito": true,
  "mensaje": "Operación exitosa",
  "datos": {
    // Response data here
  }
}

Error Response

{
  "exito": false,
  "mensaje": "Error en la operación",
  "detalle": "Additional error details or null"
}
The detalle field is only populated in debug mode or for validation errors to avoid exposing sensitive information.

HTTP Status Codes

The API uses standard HTTP status codes with specific meanings:
200 OK
success
Standard successful response for GET, PUT, PATCH operationsExample:
{
  "exito": true,
  "mensaje": "Solicitud obtenida exitosamente",
  "datos": { ... }
}
201 Created
success
Successful resource creation (POST operations)Example:
{
  "exito": true,
  "mensaje": "Registro creado exitosamente",
  "datos": { "id": 123, ... }
}
Generated by: created_response() (response_handler.py:29)
400 Bad Request
error
Invalid request data or business rule violationCommon Causes:
  • Invalid foreign key references
  • Business logic violations
  • Missing required related data
Example:
{
  "exito": false,
  "mensaje": "El solicitante y el aprobador no pueden ser la misma persona",
  "detalle": null
}
404 Not Found
error
Requested resource does not exist or has been deletedExample:
{
  "exito": false,
  "mensaje": "Solicitud con Id 999 no encontrada",
  "detalle": null
}
Generated by: not_found_response() (response_handler.py:52)
409 Conflict
error
Resource conflict or invalid state transitionCommon Causes:
  • Attempting operation on wrong state
  • Duplicate unique values
  • Conflicting business rules
Example:
{
  "exito": false,
  "mensaje": "Solo se pueden actualizar solicitudes en estado PENDIENTE_APROBACION",
  "detalle": null
}
Generated by: conflict_response() (response_handler.py:59)
422 Unprocessable Entity
error
Request validation failed (Pydantic errors)Example:
{
  "exito": false,
  "mensaje": "Error de validación en los datos enviados",
  "detalle": [
    "El campo 'asunto' es obligatorio",
    "El campo 'cantidadPasajeros' debe ser mayor o igual al mínimo permitido"
  ]
}
Generated by: Custom validation handler (main.py:70)
500 Internal Server Error
error
Unexpected server-side errorExample (Debug Mode):
{
  "exito": false,
  "mensaje": "Error interno del servidor",
  "detalle": "Connection timeout to database"
}
Example (Production):
{
  "exito": false,
  "mensaje": "Error interno del servidor",
  "detalle": null
}
Stack traces and detailed error messages are only shown when API_DEBUG=true (main.py:135)

Validation Error Handling

The API uses Pydantic for request validation with custom error translation to Spanish.

Validation Exception Handler

All Pydantic validation errors are intercepted and translated (main.py:70):
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    """Captures Pydantic validation errors and returns them in Spanish"""
    errores = []
    
    for error in exc.errors():
        campo = " → ".join(str(loc) for loc in error["loc"] if loc != "body")
        tipo = error["type"]
        msg_original = error["msg"]
        
        # Translate common messages
        mensaje = _traducir_error_validacion(campo, tipo, msg_original, error)
        errores.append(mensaje)
    
    return JSONResponse(
        status_code=422,
        content={
            "exito": False,
            "mensaje": "Error de validación en los datos enviados",
            "detalle": errores
        }
    )

Validation Error Translations

Common Pydantic errors are translated to Spanish (main.py:94):
English: Field requiredSpanish: El campo '{campo}' es obligatorioExample:
{
  "exito": false,
  "mensaje": "Error de validación en los datos enviados",
  "detalle": [
    "El campo 'asunto' es obligatorio",
    "El campo 'cantidadPasajeros' es obligatorio"
  ]
}

Service-Level Error Handling

Services return error dictionaries that routers convert to HTTP responses:

Error Dictionary Format

# Service error response
{
    "error": "Human-readable error message in Spanish",
    "status": 400  # HTTP status code
}

# Service success response
{
    "data": { ... }  # Response data
}

Common Validation Patterns

def _validar_fk_existe(self, tabla: str, id_registro: int) -> bool:
    """Validates that a foreign key reference exists and is not deleted"""
    query = f"SELECT Id FROM {tabla} WHERE Id = %s AND Eliminado = 0"
    resultado = self.db.select(query, (id_registro,))
    return bool(resultado)

# Usage
if not self._validar_fk_existe("Perfil", id_solicitante):
    return {
        "error": f"El perfil solicitante con Id {id_solicitante} no existe o está inactivo",
        "status": 400
    }

Database Error Handling

The database layer handles connection errors and SQL exceptions gracefully.

SQL Server Error Handling

The SQLServerManager class catches and logs database errors (sql_connection.py:203):
try:
    cursor.execute(query, params)
    conn.commit()
    return cursor.rowcount
    
except pymssql.Error as e:
    logger.error(f"✗ Error SQL: {e}", exc_info=True)
    if conn:
        conn.rollback()
        logger.info("✓ Rollback ejecutado")
    return [] if fetch else 0
    
except Exception as e:
    logger.error(f"✗ Error inesperado: {e}", exc_info=True)
    if conn:
        conn.rollback()
    return [] if fetch else 0
All database operations support automatic rollback on error, maintaining data integrity.

Connection Validation

Database configuration is validated on startup (sql_connection.py:47):
def _validar_configuracion(self):
    """Validates that all required configurations are present"""
    missing = []
    
    if not self.server:
        missing.append("SQL_HOST")
    if not self.database:
        missing.append("SQL_NAME")
    if not self.username:
        missing.append("SQL_USER")
    if not self.password:
        missing.append("SQL_PASSWORD")
    
    if missing:
        raise EnvironmentError(
            f"Faltan configuraciones de SQL Server en .env: {', '.join(missing)}"
        )

Global Exception Handler

Unhandled exceptions are caught by a global handler (main.py:127):
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
    """Captures any unhandled exception"""
    return JSONResponse(
        status_code=500,
        content={
            "exito": False,
            "mensaje": "Error interno del servidor",
            "detalle": str(exc) if settings.app.DEBUG else None
        }
    )
In production (API_DEBUG=false), exception details are hidden to prevent information leakage.

Error Response Examples

Validation Error (422)

POST /api/solicitud
{
  "asunto": "",
  "cantidadPasajeros": -1,
  "fechaServicioRequerido": "invalid-date"
}

Business Logic Error (400)

POST /api/solicitud
{
  "idUsuarioSolicitante": 5,
  "idUsuarioAprobador": 5,
  ...
}

Not Found Error (404)

GET /api/solicitud/99999

State Conflict Error (409)

PUT /api/solicitud/123
{
  "asunto": "Updated subject"
}

Internal Server Error (500)

{
  "exito": false,
  "mensaje": "Error interno del servidor",
  "detalle": "Connection to database timed out after 30 seconds"
}

Logging

All errors are logged with appropriate severity levels:
logger.error(f"Error al crear Solicitud: {e}")
logger.warning(f"Error al preparar correos: {e_mail}")
From: solicitud_service.py:409, 404
Log level is configurable via API_LOG_LEVEL environment variable (config.py:70)

Error Prevention Best Practices

Validate Early

Validate foreign keys and business rules before database operations

Use Transactions

Wrap multi-step operations in transactions with rollback support

Clear Messages

Provide specific, actionable error messages in the user’s language

Log Everything

Log errors with context but hide sensitive details from API responses

Handling Errors in Client Applications

When consuming this API, clients should:
1

Check exito Field

Always check the exito boolean before processing response data
if (response.exito) {
  // Handle success
  processData(response.datos);
} else {
  // Handle error
  showError(response.mensaje, response.detalle);
}
2

Handle Status Codes

Implement appropriate logic for different HTTP status codes
  • 400, 422: Show validation errors to user
  • 404: Display “not found” message
  • 409: Explain state conflict
  • 500: Show generic error and retry
3

Display Validation Details

For 422 errors, show the array of validation messages from detalle
if (response.status === 422 && Array.isArray(response.detalle)) {
  response.detalle.forEach(error => {
    displayValidationError(error);
  });
}
4

Implement Retry Logic

For 500 errors, implement exponential backoff retry
async function retryRequest(requestFn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await requestFn();
    } catch (error) {
      if (error.status !== 500 || i === maxRetries - 1) {
        throw error;
      }
      await delay(Math.pow(2, i) * 1000);
    }
  }
}

Debugging Errors

When troubleshooting API errors:
Set API_DEBUG=true in .env to see detailed error messages and stack traces
Never enable debug mode in production environments
Review application logs for detailed error context:
# View recent errors
tail -f logs/api.log | grep ERROR

# Set log level
API_LOG_LEVEL=DEBUG
Verify database connectivity:
from database.sql_connection import get_sql_manager

db = get_sql_manager()
if db.test_connection():
    print("Connection successful")
else:
    print("Connection failed")
From: sql_connection.py:441
Check that all required environment variables are set:
from config import get_settings

settings = get_settings()
errors = settings.validate()

if errors:
    for error in errors:
        print(f"Configuration error: {error}")
From: config.py:161

Next Steps

Architecture

Learn about the system architecture and design patterns

API Reference

Explore detailed API endpoint documentation

Build docs developers (and LLMs) love