Skip to main content
BackAnt uses a custom APIException class for all expected errors. Raise it from any layer — the error handler registered in app.py catches it and returns a formatted JSON response.

APIException

# api/helper/execution_tracking/APIException.py

class APIException(Exception):
    def __init__(self, message=None, status_code=None, payload=None):
        self.status_code: int = 500
        self.message: str = "Ocurrió un error"
        super().__init__()
        if message is not None:
            self.message = message
        if status_code is not None:
            self.status_code = status_code
        self.payload = payload

    def to_dict(self):
        exception = dict(self.payload or ())
        exception["message"] = self.message
        return exception

Raising errors

Import APIException in your service and raise it for expected error conditions:
from helper.execution_tracking.APIException import APIException

class UsersService:
    def get_user(self, user_id):
        user = self.users_repository.get_by_id(user_id)
        if user is None:
            raise APIException(status_code=404, message="User not found")
        return user

    def create_user(self, data):
        if not data.get("email"):
            raise APIException(status_code=400, message="Email is required")
        return self.users_repository.add_user(**data)

How it reaches the client

app.py registers APIException with Flask’s error handler:
@app.errorhandler(APIException)
def invalid_api_usage(exception):
    return jsonify(exception.to_dict()), exception.status_code
Any APIException raised anywhere in the request lifecycle (route, service, repository) is automatically caught and returned as:
HTTP 404
{"message": "User not found"}

Passing extra data

Use payload to include additional fields in the error response:
raise APIException(
    status_code=422,
    message="Validation failed",
    payload={"field": "email", "detail": "already exists"}
)
Response:
HTTP 422
{
  "message": "Validation failed",
  "field": "email",
  "detail": "already exists"
}

Common status codes

CodeUse case
400Bad request — missing or invalid input
401Unauthorized — missing or invalid token
403Forbidden — valid token but insufficient role
404Not found — resource does not exist
409Conflict — e.g. duplicate email
422Unprocessable — validation failed
500Internal server error (default)

Handling database errors

Wrap repository calls in the service when you need to translate database exceptions:
from sqlalchemy.exc import IntegrityError

def create_user(self, data):
    try:
        return self.users_repository.add_user(**data)
    except IntegrityError:
        raise APIException(status_code=409, message="Email already exists")

Unhandled exceptions

Any exception that is not an APIException will propagate as a 500 Internal Server Error. Use myLogger.exception(e) to log the full traceback before re-raising or wrapping:
from helper.execution_tracking.Logger import myLogger

try:
    result = self.users_repository.complex_operation()
except Exception as e:
    myLogger.exception(e)
    raise APIException(status_code=500, message="Unexpected error")