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)
Code Status Usage 200OK Successful GET, PUT, PATCH, DELETE 201Created Successful POST that creates a resource
Client Error Codes (4xx)
Code Status Usage 400Bad Request Invalid request format or parameters 401Unauthorized Missing or invalid authentication token 403Forbidden Authenticated but lacking permissions 404Not Found Resource doesn’t exist 409Conflict Resource conflict (duplicate email, CPF, etc.) 422Unprocessable Entity Validation error
Server Error Codes (5xx)
Code Status Usage 500Internal Server Error Unexpected server error
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
Handle All Error Codes
Implement handlers for 401, 403, 404, 409, and 5xx errors in your client.
Display User-Friendly Messages
Translate technical error messages into actionable user guidance.
Implement Retry Logic
Retry requests for transient 5xx errors with exponential backoff.
Refresh Tokens Automatically
Detect 401 errors and attempt token refresh before prompting re-authentication.
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_45 6>
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_toke n >
Response:
{
"detail" : "Could not validate credentials"
}
Status: 401 Unauthorized
Next Steps
Authentication Learn how to handle authentication errors
Authorization Understand permission-based errors