fix: handle missing files in send_message_to_user#8105
fix: handle missing files in send_message_to_user#8105idiotsj wants to merge 1 commit intoAstrBotDevs:masterfrom
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces file existence validation for media attachments and implements robust error handling for proactive message delivery. It also adds unit tests to verify these error scenarios. The feedback suggests refactoring the repeated path resolution and file check logic into a single helper method to minimize code duplication across various message components.
| def _require_existing_file(self, resolved_path: str, original_path: str) -> str: | ||
| if os.path.isfile(resolved_path): | ||
| return resolved_path | ||
| raise FileNotFoundError( | ||
| errno.ENOENT, | ||
| os.strerror(errno.ENOENT), | ||
| original_path, | ||
| ) |
There was a problem hiding this comment.
To reduce code duplication across the different message types, consider combining the path resolution and existence check into a single helper method. This aligns with the general rule of refactoring similar functionality into shared helpers. Additionally, ensure that this new attachment handling functionality is accompanied by corresponding unit tests.
| def _require_existing_file(self, resolved_path: str, original_path: str) -> str: | |
| if os.path.isfile(resolved_path): | |
| return resolved_path | |
| raise FileNotFoundError( | |
| errno.ENOENT, | |
| os.strerror(errno.ENOENT), | |
| original_path, | |
| ) | |
| async def _resolve_and_require_file(self, context: ContextWrapper[AstrAgentContext], path: str) -> str: | |
| local_path, _ = await self._resolve_path_from_sandbox(context, path) | |
| if os.path.isfile(local_path): | |
| return local_path | |
| raise FileNotFoundError( | |
| errno.ENOENT, | |
| os.strerror(errno.ENOENT), | |
| path) |
References
- When implementing similar functionality for different cases (e.g., direct vs. quoted attachments), refactor the logic into a shared helper function to avoid code duplication.
- New functionality, such as handling attachments, should be accompanied by corresponding unit tests.
| local_path, _ = await self._resolve_path_from_sandbox( | ||
| context, path | ||
| ) | ||
| local_path = self._require_existing_file(local_path, path) |
There was a problem hiding this comment.
Use the suggested _resolve_and_require_file helper to simplify the code and reduce duplication, following the repository's refactoring guidelines.
local_path = await self._resolve_and_require_file(context, path)References
- When implementing similar functionality for different cases (e.g., direct vs. quoted attachments), refactor the logic into a shared helper function to avoid code duplication.
| local_path, _ = await self._resolve_path_from_sandbox( | ||
| context, path | ||
| ) | ||
| local_path = self._require_existing_file(local_path, path) |
There was a problem hiding this comment.
Use the suggested _resolve_and_require_file helper to simplify the code and reduce duplication, following the repository's refactoring guidelines.
local_path = await self._resolve_and_require_file(context, path)References
- When implementing similar functionality for different cases (e.g., direct vs. quoted attachments), refactor the logic into a shared helper function to avoid code duplication.
| local_path, _ = await self._resolve_path_from_sandbox( | ||
| context, path | ||
| ) | ||
| local_path = self._require_existing_file(local_path, path) |
There was a problem hiding this comment.
Use the suggested _resolve_and_require_file helper to simplify the code and reduce duplication, following the repository's refactoring guidelines.
local_path = await self._resolve_and_require_file(context, path)References
- When implementing similar functionality for different cases (e.g., direct vs. quoted attachments), refactor the logic into a shared helper function to avoid code duplication.
| local_path, _ = await self._resolve_path_from_sandbox( | ||
| context, path | ||
| ) | ||
| local_path = self._require_existing_file(local_path, path) |
There was a problem hiding this comment.
Use the suggested _resolve_and_require_file helper to simplify the code and reduce duplication, following the repository's refactoring guidelines.
local_path = await self._resolve_and_require_file(context, path)References
- When implementing similar functionality for different cases (e.g., direct vs. quoted attachments), refactor the logic into a shared helper function to avoid code duplication.
Summary
send_message_to_usercomponentserror:tool results instead of letting them escape the agent loopFileNotFoundErrorProblem
When an Agent called
send_message_to_userwith a nonexistent local image path, aFileNotFoundErrorcould bubble out of the tool call path. In practice this could terminate the current agent flow before the model had a chance to fallback to a plain-text reply.Root Cause
send_message_to_useralready wrapped component construction, but it did not explicitly reject unresolved file paths before building file-based components, and it also let exceptions from the finalsend_message(...)call propagate unless the lower layer swallowed them.Fix
image/record/video/filepath targets to exist after sandbox/local resolutionerror:tool result when the final proactive send raises, includingFileNotFoundErrorVerification
python -m pytest tests/unit/test_message_tools.py -q8 passedNotes
This is intentionally a minimal defensive fix so the LLM can receive the tool error and choose a fallback response instead of having the tool exception abort the run.
Summary by Sourcery
Handle missing local file paths in send_message_to_user and return tool-level error results instead of propagating exceptions.
Bug Fixes:
Tests: