Skip to content

Conversation

@shanthanu47
Copy link

@shanthanu47 shanthanu47 commented Dec 12, 2025

Description

This PR fixes a critical bug where async tool failures would cause the Agent to crash with an unhandled exception.

The Issue:
The handle_exception wrapper in pydantic_agent.py was synchronous-only. Since most tools are async, exceptions raised during await were not being caught by the wrapper.

The Fix:
I updated handle_exception to detect inspect.iscoroutinefunction(tool_func) and return an async wrapper that properly awaits the tool execution within the try/except block.

Test Plan:
Verified using a reproduction script that launches an async tool which raises an exception. The new wrapper successfully catches it and returns a descriptive error message to the LLM.

Summary by CodeRabbit

  • Bug Fixes & Improvements
    • Enhanced error handling for both synchronous and asynchronous tool functions, providing clearer error messages to improve debugging and issue resolution.
    • Improved repository registration reliability by implementing cross-process synchronization to safely handle concurrent operations and prevent metadata conflicts when multiple processes access repositories simultaneously.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings December 12, 2025 21:01
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 12, 2025

Warning

Rate limit exceeded

@shanthanu47 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 2 minutes and 1 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 25e533c and 517bbe2.

📒 Files selected for processing (1)
  • app/modules/intelligence/agents/chat_agents/pydantic_agent.py (1 hunks)

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR enhances error handling for tool functions in the chat agent to support both sync and async contexts with improved logging, and adds cross-process locking to repository registration to prevent concurrent write collisions. It also introduces portalocker as a dependency to support the locking mechanism.

Changes

Cohort / File(s) Summary
Exception handling enhancement
app/modules/intelligence/agents/chat_agents/pydantic_agent.py
Enhanced handle_exception decorator to distinguish between sync and async tool functions, providing context-specific wrapper logic and returning explicit "Tool execution error" messages with appropriate logging for each context.
Concurrent-safe repository registration
app/modules/repo_manager/repo_manager.py
requirements.txt
Added cross-process file locking via portalocker to the repository registration flow, ensuring atomic metadata writes and preventing concurrent registration collisions. Directory creation now occurs before lock acquisition.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • pydantic_agent.py: Verify async/sync wrapper logic correctly handles exceptions and logging for both function types
  • repo_manager.py: Confirm portalocker lock scope and error handling are correct; validate directory creation precedes lock acquisition
  • requirements.txt: Ensure portalocker version 3.1.0 is compatible with the existing environment

Possibly related PRs

Suggested reviewers

  • nndn
  • dhirenmathur

Poem

🐰 Async and sync now dance in grace,
With locks ensuring their rightful place,
Exceptions logged with context clear,
Concurrent writes need never fear—
A rabbit hops through safer code! 🔒

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix/agent tool error handling' directly addresses the main change in the PR: fixing async tool error handling in the agent. It accurately summarizes the primary bug fix.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses a critical bug in async tool exception handling and adds file locking for concurrent repository registration. The main fix ensures that exceptions in async tools are properly caught and returned as error messages instead of crashing the agent.

Key changes:

  • Updated handle_exception wrapper in pydantic_agent.py to detect and handle async functions using inspect.iscoroutinefunction()
  • Added file locking in register_repo() using portalocker to prevent race conditions during concurrent repository registrations
  • Added portalocker==3.1.0 as a new dependency for cross-platform file locking

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.

File Description
app/modules/intelligence/agents/chat_agents/pydantic_agent.py Fixed async exception handling by adding coroutine detection and separate async/sync wrappers with proper await statements
app/modules/repo_manager/repo_manager.py Added file locking mechanism to prevent concurrent registration conflicts for the same repository
requirements.txt Added portalocker dependency for cross-platform file locking support

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

# Use portalocker for cross-platform file locking
# Prevent concurrent registrations for the same repo key
# if another process is currently registering it.
with portalocker.Lock(lock_file_path, timeout=10) as locked_file:
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

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

The variable locked_file returned by the context manager is not used. You can replace as locked_file with just using the context manager without capturing the return value, or use as _ to indicate it's intentionally unused.

Suggested change
with portalocker.Lock(lock_file_path, timeout=10) as locked_file:
with portalocker.Lock(lock_file_path, timeout=10):

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c725a6f and 25e533c.

📒 Files selected for processing (3)
  • app/modules/intelligence/agents/chat_agents/pydantic_agent.py (1 hunks)
  • app/modules/repo_manager/repo_manager.py (2 hunks)
  • requirements.txt (1 hunks)
🧰 Additional context used
🪛 GitHub Check: SonarCloud Code Analysis
app/modules/repo_manager/repo_manager.py

[warning] 326-326: Remove the unused local variable "locked_file".

See more on https://sonarcloud.io/project/issues?id=getmomentum_momentum-server&issues=AZsUX6ZjRvwlBIXvczsv&open=AZsUX6ZjRvwlBIXvczsv&pullRequest=529

🪛 Ruff (0.14.8)
app/modules/repo_manager/repo_manager.py

326-326: Local variable locked_file is assigned to but never used

Remove assignment to unused variable locked_file

(F841)

app/modules/intelligence/agents/chat_agents/pydantic_agent.py

55-55: Do not catch blind exception: Exception

(BLE001)


56-56: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


57-57: Use explicit conversion flag

Replace with conversion flag

(RUF010)


63-63: Do not catch blind exception: Exception

(BLE001)


64-64: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


65-65: Use explicit conversion flag

Replace with conversion flag

(RUF010)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Agent
🔇 Additional comments (1)
requirements.txt (1)

176-176: Verify new dependency pin (portalocker==3.1.0) is intentional and compatible.

Since this adds a new runtime dependency, please confirm it matches your supported Python versions and deployment environment constraints (and that the exact pinned version is the desired one).

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

app/modules/repo_manager/repo_manager.py:326

  • The variable locked_file assigned by the context manager is never used. While the file locking still works correctly, you can simplify the code by not assigning the unused variable.
        entry = self._load_metadata_entry(repo_name, branch, commit_id)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

try:
return await tool_func(*args, **kwargs)
except Exception as e:
logger.exception("Exception in async tool function", tool_name=tool_func.__name__)
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

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

The standard Python logging module's exception() method doesn't accept keyword arguments like tool_name. This will raise a TypeError at runtime. Instead, include the tool name in the message string itself.

Copilot uses AI. Check for mistakes.
try:
return tool_func(*args, **kwargs)
except Exception as e:
logger.exception("Exception in sync tool function", tool_name=tool_func.__name__)
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

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

The standard Python logging module's exception() method doesn't accept keyword arguments like tool_name. This will raise a TypeError at runtime. Instead, include the tool name in the message string itself.

Copilot uses AI. Check for mistakes.
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant