Skip to main content

Overview

VidaPlus API follows REST best practices for error handling, using standard HTTP status codes and structured error responses to help you diagnose and handle errors effectively.

HTTP Status Codes

The API uses standard HTTP status codes to indicate success or failure:

Success Codes (2xx)

CodeStatusUsage
200OKSuccessful GET, PUT, PATCH, DELETE
201CreatedSuccessful POST that creates a resource

Client Error Codes (4xx)

CodeStatusUsage
400Bad RequestInvalid request format or parameters
401UnauthorizedMissing or invalid authentication token
403ForbiddenAuthenticated but lacking permissions
404Not FoundResource doesn’t exist
409ConflictResource conflict (duplicate email, CPF, etc.)
422Unprocessable EntityValidation error

Server Error Codes (5xx)

CodeStatusUsage
500Internal Server ErrorUnexpected server error

Error Response Format

All errors return a JSON response with a consistent structure:
{
  "detail": "Error message describing what went wrong"
}

Common Error Patterns

Authentication Errors (401)

Occur when authentication fails or tokens are invalid:
credentials_exception = HTTPException(
    status_code=HTTPStatus.UNAUTHORIZED,
    detail='Could not validate credentials',
    headers={'WWW-Authenticate': 'Bearer'},
)
Example Response:
{
  "detail": "Could not validate credentials"
}
Common Causes:
  • Missing Authorization header
  • Expired access token
  • Invalid token format
  • Token signature verification failed
Solution:
try:
    response = requests.get(
        'https://api.vidaplus.com/pacientes/',
        headers={'Authorization': f'Bearer {access_token}'}
    )
    response.raise_for_status()
except requests.HTTPError as e:
    if e.response.status_code == 401:
        # Refresh token or re-authenticate
        new_token = refresh_access_token()
        # Retry request

Authorization Errors (403)

Occur when authenticated users lack permissions:
if not current_user.is_superuser:
    raise HTTPException(
        status_code=HTTPStatus.FORBIDDEN,
        detail='Apenas usuários com permissão de administrador podem criar pacientes.',
    )
Example Response:
{
  "detail": "Você não tem permissão para acessar este usuário"
}
Common Causes:
  • Non-admin trying to perform admin operations
  • User accessing another user’s resources
  • Professional accessing resources outside their assignments

Resource Not Found (404)

user = session.get(PacienteUser, user_id)
if not user:
    raise HTTPException(
        status_code=HTTPStatus.NOT_FOUND,
        detail='User not found'
    )
Example Response:
{
  "detail": "Consulta não encontrada."
}
Common Causes:
  • Invalid resource ID
  • Resource was deleted
  • User lacks permission (404 used instead of 403 for security)

Conflict Errors (409)

Occur when creating resources with duplicate unique fields:
db_user = session.scalar(
    select(PacienteUser).where(
        (PacienteUser.email == user.email) | (PacienteUser.cpf == user.cpf)
    )
)

if db_user:
    if db_user.email == user.email:
        raise HTTPException(
            status_code=HTTPStatus.CONFLICT,
            detail='Email already exists',
        )
    elif db_user.cpf == user.cpf:
        raise HTTPException(
            status_code=HTTPStatus.CONFLICT,
            detail='CPF already exists',
        )
Example Response:
{
  "detail": "Email already exists"
}
Common Causes:
  • Duplicate email address
  • Duplicate CPF
  • Duplicate CRM/COREN
  • Duplicate appointment (same time, patient, professional)

Domain-Specific Error Handling

Appointment Conflicts

Preventing duplicate appointments:
db_consulta = session.scalar(
    select(Consulta).where(
        (Consulta.data == consulta.data)
        & (Consulta.hora == consulta.hora)
        & (Consulta.paciente_id == consulta.paciente_id)
        & (Consulta.profissional_id == consulta.profissional_id)
    )
)

if db_consulta:
    detail = 'Já existe uma consulta agendada para este paciente e profissional na mesma data e hora.'
    raise HTTPException(
        status_code=HTTPStatus.CONFLICT,
        detail=detail,
    )

Foreign Key Validation

Validating related resources exist before creation:
paciente_id = session.scalar(
    select(PacienteUser).where(PacienteUser.id == consulta.paciente_id)
)
profissional_id = session.scalar(
    select(ProfissionalUser).where(
        ProfissionalUser.id == consulta.profissional_id,
    )
)
prontuario_id = session.scalar(
    select(Prontuario).where(Prontuario.id == consulta.prontuario_id)
)

if not profissional_id:
    raise HTTPException(
        status_code=HTTPStatus.NOT_FOUND,
        detail='Profissional não encontrado.',
    )

if not paciente_id:
    raise HTTPException(
        status_code=HTTPStatus.NOT_FOUND,
        detail='Paciente não encontrado.',
    )

if not prontuario_id:
    raise HTTPException(
        status_code=HTTPStatus.NOT_FOUND,
        detail='Prontuário não encontrado.',
    )

Database Integrity Errors

Handling constraint violations during updates:
from sqlalchemy.exc import IntegrityError

try:
    user_to_update.nome = user.nome
    user_to_update.email = user.email
    user_to_update.cpf = user.cpf
    
    session.commit()
    session.refresh(user_to_update)
    
    return user_to_update
except IntegrityError:
    raise HTTPException(
        status_code=HTTPStatus.CONFLICT,
        detail='CPF or Email already exists',
    )

Error Handling in Client Applications

Python Example

import requests
from typing import Optional

def get_patient(patient_id: int, access_token: str) -> Optional[dict]:
    """Get patient with comprehensive error handling."""
    try:
        response = requests.get(
            f'https://api.vidaplus.com/pacientes/{patient_id}',
            headers={'Authorization': f'Bearer {access_token}'}
        )
        response.raise_for_status()
        return response.json()
        
    except requests.HTTPError as e:
        if e.response.status_code == 401:
            print('Authentication failed. Please login again.')
            # Trigger re-authentication
        elif e.response.status_code == 403:
            print('You do not have permission to access this patient.')
        elif e.response.status_code == 404:
            print(f'Patient {patient_id} not found.')
        else:
            error_detail = e.response.json().get('detail', 'Unknown error')
            print(f'Error: {error_detail}')
        return None
        
    except requests.RequestException as e:
        print(f'Network error: {str(e)}')
        return None

JavaScript Example

async function getPatient(patientId, accessToken) {
  try {
    const response = await fetch(
      `https://api.vidaplus.com/pacientes/${patientId}`,
      {
        headers: {
          'Authorization': `Bearer ${accessToken}`
        }
      }
    );

    if (!response.ok) {
      const error = await response.json();
      
      switch (response.status) {
        case 401:
          console.error('Authentication failed. Please login again.');
          // Trigger re-authentication
          break;
        case 403:
          console.error('You do not have permission to access this patient.');
          break;
        case 404:
          console.error(`Patient ${patientId} not found.`);
          break;
        default:
          console.error(`Error: ${error.detail}`);
      }
      
      return null;
    }

    return await response.json();
    
  } catch (error) {
    console.error('Network error:', error);
    return null;
  }
}

Validation Errors

FastAPI automatically validates request data using Pydantic schemas and returns 422 errors: Example Invalid Request:
{
  "nome": "João Silva",
  "email": "invalid-email",
  "cpf": "123"
}
Response:
{
  "detail": [
    {
      "loc": ["body", "email"],
      "msg": "value is not a valid email address",
      "type": "value_error.email"
    },
    {
      "loc": ["body", "cpf"],
      "msg": "ensure this value has at least 11 characters",
      "type": "value_error.any_str.min_length"
    }
  ]
}

Logging and Monitoring

VidaPlus implements structured logging for error tracking:
from vidaplus.logger import get_logger

logger = get_logger("consultas")

if not profissional_id:
    logger.resource_not_found(
        resource_type="profissional",
        resource_id=str(consulta.profissional_id),
        user_id=str(current_user.id),
        operation="create_consulta"
    )
    raise HTTPException(
        status_code=HTTPStatus.NOT_FOUND,
        detail='Profissional não encontrado.',
    )
Logs include contextual information like user IDs and resource IDs for debugging and security auditing.

Best Practices

1

Handle All Error Codes

Implement handlers for 401, 403, 404, 409, and 5xx errors in your client.
2

Display User-Friendly Messages

Translate technical error messages into actionable user guidance.
3

Implement Retry Logic

Retry requests for transient 5xx errors with exponential backoff.
4

Refresh Tokens Automatically

Detect 401 errors and attempt token refresh before prompting re-authentication.
5

Log Errors

Log all errors with context for debugging and monitoring.

Error Handling Checklist

Authentication

✅ Handle 401 errors ✅ Implement token refresh ✅ Provide re-authentication flow

Authorization

✅ Handle 403 errors ✅ Show permission-based UI ✅ Explain permission requirements

Validation

✅ Handle 422 errors ✅ Display field-specific errors ✅ Validate client-side first

Conflicts

✅ Handle 409 errors ✅ Suggest resolution (e.g., “Email already in use”) ✅ Allow users to correct input

Common Error Scenarios

Scenario 1: Creating User with Existing Email

Request:
POST /pacientes/
{
  "nome": "João Silva",
  "email": "[email protected]",
  "cpf": "12345678901"
}
Response:
{
  "detail": "Email already exists"
}
Status: 409 Conflict

Scenario 2: Accessing Another User’s Data

Request:
GET /pacientes/123
Authorization: Bearer <patient_token_for_user_456>
Response:
{
  "detail": "Você não tem permissão para acessar este usuário"
}
Status: 403 Forbidden

Scenario 3: Token Expired

Request:
GET /pacientes/
Authorization: Bearer <expired_token>
Response:
{
  "detail": "Could not validate credentials"
}
Status: 401 Unauthorized

Next Steps

Authentication

Learn how to handle authentication errors

Authorization

Understand permission-based errors

Build docs developers (and LLMs) love