Skip to content

Commit 726e74f

Browse files
Truncate FailureResponse.message before appending addr suffix in _failure_text
``OperationalError._MAX_DISPLAY_MESSAGE`` (1024 codepoints) caps the display ``message`` field. ``DqliteProtocol._failure_text`` appended the peer-address suffix AFTER the server message. For server messages >1 KiB (ORM-generated SQL with many bound parameters, large constraint-violation diagnostics, etc.), the suffix landed in the truncated tail — operators tailing logs via ``logger.error("%s", exc)`` saw the truncated server text but lost the ``" to <peer>"`` attribution that makes multi-node diagnosis tractable. Apply the per-node ``_truncate_error`` cap (200 codepoints, already used in cluster aggregate-error rendering) to ``response.message`` BEFORE appending the addr suffix. The ``_MAX_DISPLAY_MESSAGE`` cap in ``OperationalError`` stays in place as a defense-in-depth net for any caller that constructs from raw text outside this helper, but the common case now arrives pre-truncated with the suffix preserved. Pairs with the X2 ``raw_message`` kwarg fix: the verbatim server text continues to flow through ``e.raw_message`` so forensic / log-aggregator views still have the un-truncated copy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5b67aa1 commit 726e74f

2 files changed

Lines changed: 44 additions & 1 deletion

File tree

src/dqliteclient/protocol.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,8 +749,21 @@ def _failure_text(self, response: FailureResponse) -> str:
749749
:func:`_failure_message` with the protocol's ``_addr_suffix``.
750750
Used uniformly across the query-path raise sites so the
751751
rendering is consistent.
752+
753+
Truncates the server message BEFORE composing the addr
754+
suffix so the suffix survives the
755+
``OperationalError._MAX_DISPLAY_MESSAGE`` codepoint cap on
756+
the exception's display ``message`` field. Without
757+
pre-truncation, a long server message (e.g. ORM-generated
758+
SQL with many bound parameters) would push the suffix past
759+
the cutoff — operators tailing logs via
760+
``logger.error("%s", exc)`` would see the truncated server
761+
text but lose the peer-address attribution.
752762
"""
753-
return _failure_message(response.message, self._addr_suffix())
763+
from dqliteclient.cluster import _truncate_error
764+
765+
truncated_msg = _truncate_error(response.message)
766+
return _failure_message(truncated_msg, self._addr_suffix())
754767

755768
def _operation_deadline(self) -> float:
756769
"""Deadline (monotonic seconds) for a single read-side protocol

tests/test_operationalerror_raw_message_clean.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,33 @@ def test_operational_error_truncation_preserves_raw_message_full_length() -> Non
4848
err = OperationalError(1, long_text, raw_message=long_text)
4949
assert len(err.raw_message) == 4096
5050
assert "[truncated," in err.message
51+
52+
53+
def test_failure_text_truncates_message_before_appending_addr_suffix() -> None:
54+
"""Pin: ``DqliteProtocol._failure_text`` truncates the server
55+
message BEFORE appending the addr suffix so the suffix
56+
survives the ``_MAX_DISPLAY_MESSAGE`` codepoint cap on the
57+
exception's display ``message`` field. Without pre-
58+
truncation, a 100k-char ORM-generated SQL error would
59+
push the addr suffix past the cutoff and operators
60+
tailing logs lose the peer-address attribution.
61+
"""
62+
from unittest.mock import AsyncMock, MagicMock
63+
64+
from dqliteclient.protocol import DqliteProtocol
65+
from dqlitewire.messages import FailureResponse
66+
67+
proto = DqliteProtocol(
68+
AsyncMock(spec=["read"]),
69+
MagicMock(spec=["close", "wait_closed"]),
70+
address="some-host:9001",
71+
)
72+
huge = "x" * 100_000
73+
response = FailureResponse(code=1, message=huge)
74+
75+
rendered = proto._failure_text(response)
76+
77+
# Suffix must appear at the end of the rendered text.
78+
assert rendered.endswith(" to some-host:9001"), (
79+
f"Addr suffix must survive truncation; rendered ends with: {rendered[-100:]!r}"
80+
)

0 commit comments

Comments
 (0)