Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,27 @@ async def create_issue(
filename = f"{uuid.uuid4()}_{image.filename}"
file_location = f"data/uploads/{filename}"

# Write to disk in a threadpool to avoid blocking event loop
await run_in_threadpool(save_file_blocking, image.file, file_location)
# Offload blocking file I/O to a thread
def save_file():
with open(image_path, "wb") as buffer:
shutil.copyfileobj(image.file, buffer)
Comment on lines +235 to +238

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The image.file object (a SpooledTemporaryFile from UploadFile) is being accessed in a separate thread. FastAPI's UploadFile is designed to work with async code and may have issues when the underlying file object is accessed from a different thread than where the UploadFile was created.

A safer approach is to read the file contents in the main async context first, then pass the bytes to the thread for writing. Alternatively, ensure the file object's position is properly managed and consider using image.file.read() before passing to the thread.

Suggested change
# Offload blocking file I/O to a thread
def save_file():
with open(image_path, "wb") as buffer:
shutil.copyfileobj(image.file, buffer)
# Read the uploaded file in the async context to avoid accessing
# the underlying UploadFile.file from a separate thread.
image_data = await image.read()
# Offload blocking file I/O to a thread
def save_file():
with open(image_path, "wb") as buffer:
buffer.write(image_data)

Copilot uses AI. Check for mistakes.

# Analyze with AI
ai_analysis = await analyze_issue_image(file_location)
await asyncio.to_thread(save_file)

# Offload blocking DB operations to a thread
def save_to_db():
new_issue = Issue(
description=description,
category=category,
image_path=image_path,
source="web"
)
db.add(new_issue)
db.commit()
db.refresh(new_issue)
return new_issue

new_issue = await asyncio.to_thread(save_to_db)
Comment on lines +243 to +255

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The database session db is not thread-safe and should not be used across threads. SQLAlchemy sessions are bound to the thread that created them. When save_to_db() executes in a separate thread via asyncio.to_thread(), it uses the db session that was created in the main event loop thread, which can lead to undefined behavior, connection errors, or data corruption.

Instead, create a new database session within the save_to_db() function that will be used exclusively in that thread. You can import SessionLocal from backend.database and instantiate a new session inside the function, ensuring proper closure with a try-finally block.

Copilot uses AI. Check for mistakes.

# Generate Action Plan (AI)
action_plan = await generate_action_plan(description, category, file_location)
Expand Down
Loading