From 6ae78102a63744409142e157c0c54ca2961c67fd Mon Sep 17 00:00:00 2001 From: Andrei Ancuta Date: Tue, 1 Sep 2026 15:31:11 +0300 Subject: [PATCH] fix(agent): include the Orchestrator error message in the StartJobs 404 tool error Orchestrator returns 404 with the generic error code 1002 for several distinct lookups inside StartJobs (release not found, attachment not found, ...), and the runtime dropped the server message, making every such failure read as "Could not find process". Append the server message to the mapped error so job logs and traces name the failing lookup. Co-Authored-By: Claude Fable 5 --- .../agent/tools/process_tool.py | 43 ++++++++----- tests/agent/test_exception_helpers.py | 60 +++++++++++++++++++ uv.lock | 10 ++-- 3 files changed, 94 insertions(+), 19 deletions(-) diff --git a/src/uipath_langchain/agent/tools/process_tool.py b/src/uipath_langchain/agent/tools/process_tool.py index 721eb0fe5..dbb81ee22 100644 --- a/src/uipath_langchain/agent/tools/process_tool.py +++ b/src/uipath_langchain/agent/tools/process_tool.py @@ -26,21 +26,36 @@ from .utils import sanitize_tool_name -_START_JOBS_ERRORS: dict[tuple[int, str | None], tuple[str, UiPathErrorCategory]] = { - (404, "1002"): ( - "Could not find process for tool '{tool}'. Please check if the process is deployed in the configured folder.", - UiPathErrorCategory.DEPLOYMENT, - ), - (400, "1100"): ( - "Could not find folder for tool '{tool}'. Please check if the folder exists and is accessible by the robot.", - UiPathErrorCategory.DEPLOYMENT, - ), - (409, None): ( - "Cannot start process for tool '{tool}': {message}", - UiPathErrorCategory.DEPLOYMENT, - ), +_START_JOBS_404_TEMPLATES: dict[str, str] = { + "AssociatedProcessNotFound": "Could not find process for tool '{tool}'. Please check if the process is deployed in the configured folder.", + "AttachmentNotFound": "Could not find an attachment passed to tool '{tool}'. Please check that the attachments provided to the tool still exist.", } +_START_JOBS_404_FALLBACK_TEMPLATE = "Could not start process for tool '{tool}': an item required to start the job was not found. Server message: {message}" + + +def _start_jobs_errors( + e: EnrichedException, +) -> dict[tuple[int, str | None], tuple[str, UiPathErrorCategory]]: + server_message = (e.error_info.message if e.error_info else None) or "" + not_found_template = _START_JOBS_404_TEMPLATES.get( + server_message, _START_JOBS_404_FALLBACK_TEMPLATE + ) + return { + (404, "1002"): ( + not_found_template, + UiPathErrorCategory.DEPLOYMENT, + ), + (400, "1100"): ( + "Could not find folder for tool '{tool}'. Please check if the folder exists and is accessible by the robot.", + UiPathErrorCategory.DEPLOYMENT, + ), + (409, None): ( + "Cannot start process for tool '{tool}': {message}", + UiPathErrorCategory.DEPLOYMENT, + ), + } + def create_process_tool( resource: AgentProcessToolResourceConfig, @@ -91,7 +106,7 @@ async def start_job(): except EnrichedException as e: raise_for_enriched( e, - _START_JOBS_ERRORS, + _start_jobs_errors(e), title=f"Failed to execute tool '{resource.name}'", tool=resource.name, ) diff --git a/tests/agent/test_exception_helpers.py b/tests/agent/test_exception_helpers.py index 624ecb940..b3e5bb972 100644 --- a/tests/agent/test_exception_helpers.py +++ b/tests/agent/test_exception_helpers.py @@ -9,6 +9,7 @@ from uipath_langchain.agent.exceptions import AgentRuntimeError, AgentRuntimeErrorCode from uipath_langchain.agent.exceptions.helpers import raise_for_enriched +from uipath_langchain.agent.tools.process_tool import _start_jobs_errors def _make_enriched( @@ -138,3 +139,62 @@ def test_original_exception_chained(self) -> None: with pytest.raises(AgentRuntimeError) as exc_info: raise_for_enriched(err, _KNOWN_ERRORS, title=_TITLE, tool="T") assert exc_info.value.__cause__ is err + + +class TestStartJobsNotFound: + def test_process_not_found(self) -> None: + err = _make_enriched( + 404, + {"errorCode": "1002", "message": "AssociatedProcessNotFound"}, + ) + with pytest.raises(AgentRuntimeError) as exc_info: + raise_for_enriched( + err, _start_jobs_errors(err), title=_TITLE, tool="MyProcess" + ) + assert exc_info.value.error_info.detail == ( + "Could not find process for tool 'MyProcess'. " + "Please check if the process is deployed in the configured folder." + ) + + def test_attachment_not_found(self) -> None: + err = _make_enriched( + 404, + {"errorCode": "1002", "message": "AttachmentNotFound"}, + ) + with pytest.raises(AgentRuntimeError) as exc_info: + raise_for_enriched( + err, _start_jobs_errors(err), title=_TITLE, tool="MyProcess" + ) + detail = exc_info.value.error_info.detail + assert "Could not find process" not in detail + assert detail == ( + "Could not find an attachment passed to tool 'MyProcess'. " + "Please check that the attachments provided to the tool still exist." + ) + + def test_unknown_message_includes_server_message(self) -> None: + err = _make_enriched( + 404, + {"errorCode": "1002", "message": "SomeOtherItemNotFound"}, + ) + with pytest.raises(AgentRuntimeError) as exc_info: + raise_for_enriched( + err, _start_jobs_errors(err), title=_TITLE, tool="MyProcess" + ) + assert exc_info.value.error_info.detail == ( + "Could not start process for tool 'MyProcess': an item required " + "to start the job was not found. " + "Server message: SomeOtherItemNotFound" + ) + + def test_empty_server_message(self) -> None: + err = _make_enriched(404, {"errorCode": "1002"}) + with pytest.raises(AgentRuntimeError) as exc_info: + raise_for_enriched( + err, _start_jobs_errors(err), title=_TITLE, tool="MyProcess" + ) + assert exc_info.value.error_info.detail == ( + "Could not start process for tool 'MyProcess': an item required " + "to start the job was not found. " + "Server message: " + ) diff --git a/uv.lock b/uv.lock index 0269d6e00..9f6fe96ac 100644 --- a/uv.lock +++ b/uv.lock @@ -172,9 +172,9 @@ name = "aiologic" version = "0.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "sniffio", marker = "python_full_version < '3.13'" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, - { name = "wrapt", marker = "python_full_version < '3.13'" }, + { name = "sniffio" }, + { name = "typing-extensions" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f1/7a/d51f2fde1e8ae8a83431f8e97b7a71e9358cdb1d4d2ce6be387fa44d68de/aiologic-0.17.1.tar.gz", hash = "sha256:2e1b93b9e88ced318c2a63ad7b382688f40cbfe40e3d42258d49dc9c5aea179d", size = 252354, upload-time = "2026-06-27T20:41:33.25Z" } wheels = [ @@ -945,8 +945,8 @@ name = "culsans" version = "0.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiologic", marker = "python_full_version < '3.13'" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "aiologic" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d9/e3/49afa1bc180e0d28008ec6bcdf82a4072d1c7a41032b5b759b60814ca4b0/culsans-0.11.0.tar.gz", hash = "sha256:0b43d0d05dce6106293d114c86e3fb4bfc63088cfe8ff08ed3fe36891447fe33", size = 107546, upload-time = "2025-12-31T23:15:38.196Z" } wheels = [