When I run my code in normal way, my API endpoint returns 500. The problem is I cannot debug, as when I am running in debug mode and going line-by-line, it actually runs OK. The suspected piece of code is this method:
try:
async with httpx.AsyncClient(verify=False) as client:
response = await client.post(
config['api']['url'],
json=json.loads(json_data),
headers=headers,
)
ic(response.content)
ic(response.headers)
response.raise_for_status()
return response
except httpx.ProxyError as e:
logger.error(f"Error connecting to proxy: {e}", extra=d)
raise HTTPException(status_code=503, detail="Error connecting to proxy") from e
except httpx.HTTPStatusError as e:
logger.error(f"External API returned error (status: {e.response.status_code}): {e.response.text}", extra=d)
raise HTTPException(status_code=e.response.status_code,
detail=f"External API returned error: {e.response.text}") from e
except httpx.ConnectError as e:
logger.error(f"Could not connect to External API: {e}", extra=d)
raise HTTPException(status_code=503, detail="Could not connect to External API") from e
except httpx.RequestError as e: # General httpx request error
logger.error(f"External API request returned error: {e}", extra=d)
raise HTTPException(status_code=500, detail=f"External API request returned error: {e}") from e
except OSError as e: # This might still catch underlying OS errors
logger.error(f"OS error: {e}", extra=d)
raise HTTPException(status_code=500, detail=f"OS error: {e}") from e
ic does not produce any output. {e} stays empty. Here is how console output looks like:
ERROR::subtask_logger::External API request returned error:
INFO: 127.0.0.1:58716 - "POST /fwr/connections/order HTTP/1.1" 500 Internal Server Error
And some excerpts from files where I call this function:
async def request(self, task_id: uuid.UUID, db: Session, chunk_size=100):
...
crh = ProcessingRequestHandler(task_id)
...
processing_payload_json_string = connections_json_payload.model_dump_json()
processing_response = await crh.request(processing_payload_json_string)
...
if i > 0: # There are records to process
return task_id
else: # no records to process
return None
And finally endpoint itself:
async def request_processing(db: Session = Depends(get_db)) -> JSONResponse:
task_id = uuid4()
try:
processing = ProcessingTool()
result = await processing.request(task_id, chunk_size=100, db=db)
if result is None:
message = {"task_id": str(task_id), "status": "failure", "message": "No items to process"}
else:
message = {"task_id": str(task_id), "status": "success", "message": "Request processed successfully"}
return JSONResponse(status_code=200, content=message)
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail="Internal error while processing request") from e
It's the first time I am using asyncio, might be that I don't get something about how it works. Some names of variables and methods has been manually changed to shadow real usage and might bring some inconsistency, but I took my best effort to avoid that.
You are passing db into async without using async engine
If your stack looks like: FastAPI (async)
↓
ProcessingTool().request()
↓
ProcessingRequestHandler().request()
↓
uses SQLAlchemy Session (sync)
Then under concurrency, SQLAlchemy will block, causing httpx to time out or fail.
Debug mode serializes everything, so it passes. Your code works in debug mode because execution is slowed → timing changes → no race conditions / timeouts.