diff --git a/cognite/extractorutils/unstable/core/base.py b/cognite/extractorutils/unstable/core/base.py index d01b1f3b..6e38b9c9 100644 --- a/cognite/extractorutils/unstable/core/base.py +++ b/cognite/extractorutils/unstable/core/base.py @@ -784,9 +784,9 @@ def _handle_custom_action(self, action: Action) -> None: external_id=action.external_id, status=completed_status, result_message=truncate_message( - f"Action '{custom.name}' {outcome}, but metadata field(s) " - f"{', '.join(oversized_fields)} exceeded the {MAX_METADATA_VALUE_BYTES}-byte-per-value " - f"limit and were dropped from the reported result" + f"Action '{custom.name}' {outcome}, but {len(oversized_fields)} metadata field(s) " + f"exceeded the {MAX_METADATA_VALUE_BYTES}-byte-per-value limit and were dropped from " + f"the reported result: {', '.join(oversized_fields)}" ), result_metadata=filtered_metadata, ) @@ -813,8 +813,10 @@ def _handle_custom_action(self, action: Action) -> None: str(e) if not oversized_fields else truncate_message( - f"{e} (additionally, metadata field(s) {', '.join(oversized_fields)} exceeded " - f"the {MAX_METADATA_VALUE_BYTES}-byte-per-value limit and were dropped)" + f"{len(oversized_fields)} metadata field(s) exceeded the " + f"{MAX_METADATA_VALUE_BYTES}-byte-per-value limit and were dropped. " + f"Action failed: {truncate_message(str(e), max_length=500)}. " + f"Dropped fields: {', '.join(oversized_fields)}" ) ), result_metadata=filtered_metadata, diff --git a/tests/test_unstable/test_action_dispatch.py b/tests/test_unstable/test_action_dispatch.py index 1d29dddc..635f1e6a 100644 --- a/tests/test_unstable/test_action_dispatch.py +++ b/tests/test_unstable/test_action_dispatch.py @@ -407,6 +407,10 @@ def target(ctx: ActionContext) -> None: assert final.result_message is not None assert len(final.result_message) <= MAX_MESSAGE_LENGTH assert final.result_message.endswith("...") + # Regression: the explanation, including the count, must survive truncation even when the + # field-name list doesn't. + assert "45 metadata field(s) exceeded" in final.result_message + assert "dropped" in final.result_message def test_oversized_action_error_with_long_message_truncates_instead_of_crashing() -> None: @@ -427,7 +431,13 @@ def target(ctx: ActionContext) -> None: assert failed.result_metadata == {"error_type": "invalid_parameter"} assert failed.result_message is not None assert len(failed.result_message) <= MAX_MESSAGE_LENGTH - assert failed.result_message.endswith("...") + # Regression: the metadata-dropped explanation must survive truncation even when the + # ActionError message itself is long enough to need truncating on its own. + assert "exceeded" in failed.result_message + assert "dropped" in failed.result_message + # Regression: str(e) is bounded before combining, so there's still room left for the + # dropped-field-name list even when the ActionError message alone is very long. + assert "error_detail" in failed.result_message def test_custom_action_receives_call_metadata_in_context() -> None: