diff --git a/CHANGELOG.md b/CHANGELOG.md index 33f28a8..61b9057 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to `uipath_llm_client` (core package) will be documented in this file. +## [1.18.5] - 2026-09-07 + +### Fixed +- `UiPathAPIError.__str__` no longer includes the response body. The `traceback` module renders the final exception line with `str()`, so a relayed provider error body was copied verbatim into every printed stacktrace — including the one shipped as `AgentRun.Failed.ErrorTraceback` — regardless of what the consuming runtime chose to put in its own customer-facing error. The body is content of unknown sensitivity and may carry PII, so it now stays on the `body` attribute (unchanged, for callers that classify on it) and out of the serialized form. `__repr__` is unchanged. (PC-5002) + ## [1.18.0] - 2026-08-13 ### Changed diff --git a/packages/uipath_langchain_client/CHANGELOG.md b/packages/uipath_langchain_client/CHANGELOG.md index 526b958..0bbe841 100644 --- a/packages/uipath_langchain_client/CHANGELOG.md +++ b/packages/uipath_langchain_client/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to `uipath_langchain_client` will be documented in this file. +## [1.18.5] - 2026-09-07 + +### Changed +- Bumped the `uipath-llm-client` floor to `>=1.18.5`, which stops `UiPathAPIError.__str__` from copying the response body into printed stacktraces. (PC-5002) + ## [1.18.4] - 2026-09-02 ### Fixed diff --git a/packages/uipath_langchain_client/pyproject.toml b/packages/uipath_langchain_client/pyproject.toml index 295b6ba..4dbdd9f 100644 --- a/packages/uipath_langchain_client/pyproject.toml +++ b/packages/uipath_langchain_client/pyproject.toml @@ -6,7 +6,7 @@ readme = "README.md" requires-python = ">=3.11" dependencies = [ "langchain>=1.2.15,<2.0.0", - "uipath-llm-client>=1.18.0,<2.0.0", + "uipath-llm-client>=1.18.5,<2.0.0", ] [project.optional-dependencies] diff --git a/packages/uipath_langchain_client/src/uipath_langchain_client/__version__.py b/packages/uipath_langchain_client/src/uipath_langchain_client/__version__.py index b91c30e..c149855 100644 --- a/packages/uipath_langchain_client/src/uipath_langchain_client/__version__.py +++ b/packages/uipath_langchain_client/src/uipath_langchain_client/__version__.py @@ -1,3 +1,3 @@ __title__ = "UiPath LangChain Client" __description__ = "A Python client for interacting with UiPath's LLM services via LangChain." -__version__ = "1.18.4" +__version__ = "1.18.5" diff --git a/src/uipath/llm_client/__version__.py b/src/uipath/llm_client/__version__.py index d908879..8dacbda 100644 --- a/src/uipath/llm_client/__version__.py +++ b/src/uipath/llm_client/__version__.py @@ -1,3 +1,3 @@ __title__ = "UiPath LLM Client" __description__ = "A Python client for interacting with UiPath's LLM services." -__version__ = "1.18.0" +__version__ = "1.18.5" diff --git a/src/uipath/llm_client/utils/exceptions.py b/src/uipath/llm_client/utils/exceptions.py index 2648321..e717db2 100644 --- a/src/uipath/llm_client/utils/exceptions.py +++ b/src/uipath/llm_client/utils/exceptions.py @@ -205,10 +205,10 @@ def _parse_retry_after(response: Response) -> float | None: return None def __str__(self) -> str: - return ( - f"{self.__class__.__name__}: {self.message} " - f"(Status Code: {self.status_code}) {self.body}" - ) + # ``self.body`` is deliberately absent. The traceback module renders the + # final exception line with str(), so anything here is copied verbatim + # into every printed stacktrace -- potentially including PII + return f"{self.__class__.__name__}: {self.message} (Status Code: {self.status_code})" def __repr__(self) -> str: return ( diff --git a/tests/core/features/test_exceptions.py b/tests/core/features/test_exceptions.py index 823f504..6f73423 100644 --- a/tests/core/features/test_exceptions.py +++ b/tests/core/features/test_exceptions.py @@ -124,6 +124,34 @@ def test_str_format(self): assert "Bad Request" in s assert "400" in s + @pytest.mark.parametrize( + "body_json,body_text", + [ + ({"error": {"message": "PROVIDER_SECRET_MSG"}}, None), + (None, "PROVIDER_SECRET_MSG"), + ], + ids=["json-body", "text-body"], + ) + def test_str_omits_the_response_body(self, body_json, body_text): + """The body may relay provider content of unknown sensitivity (PC-5002). + + str() is what the traceback module prints as the final exception line, so + anything here reaches every serialized stacktrace. + """ + resp = self._make_response(400, "Bad Request", body_json=body_json, body_text=body_text) + exc = UiPathAPIError.from_response(resp) + + assert "PROVIDER_SECRET_MSG" not in str(exc) + assert str(exc) == "UiPathBadRequestError: Bad Request (Status Code: 400)" + + def test_str_omission_does_not_drop_the_body_attribute(self): + """Callers classify on .body -- it must survive the __str__ redaction.""" + body = {"error": {"message": "PROVIDER_SECRET_MSG", "param": "max_tokens"}} + resp = self._make_response(400, "Bad Request", body_json=body) + exc = UiPathAPIError.from_response(resp) + + assert exc.body == body + def test_repr_format(self): resp = self._make_response(404, "Not Found", body_json={"error": "missing"}) exc = UiPathAPIError.from_response(resp)