Skip to content
Merged
Show file tree
Hide file tree
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions packages/uipath_langchain_client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/uipath_langchain_client/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
2 changes: 1 addition & 1 deletion src/uipath/llm_client/__version__.py
Original file line number Diff line number Diff line change
@@ -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"
8 changes: 4 additions & 4 deletions src/uipath/llm_client/utils/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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})"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is removing the whole body a valid solution to this PII vs traceability trade off? excluding the whole body might be removing too much info that would be helpful when debugging. maybe it might be better to try to isolate potential PII on specific body fields and exclude those. for instance for bad request we had this discussion so it might be nice to map out exactly on what fields PII bad request can appear (https://uipath-product.slack.com/archives/C05AANXHV9S/p1788178862719219)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

given we don't know for sure which fields can contain PII then it's safer to ignore all of them.


def __repr__(self) -> str:
return (
Expand Down
28 changes: 28 additions & 0 deletions tests/core/features/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading