unbridled-41 opened a new issue, #4735:
URL: https://github.com/apache/rocketmq-dashboard/issues/4735

   ### Studio Version
   
   branch: rocketmq-studio
   git commit id: 7ce9a68215c4cd1c7025013d02135b43b6aa6ba1
   
   ### Problem
   
   The AI redelivery tool (`rmq.message.redelivery`) publishes the message 
explorer's **display projection** instead of the stored payload, so a 
redelivered copy silently differs from the source message:
   
   - the body is cut at 64 KiB 
(`RocketMQMessageProvider.MAX_BODY_DISPLAY_BYTES`, 
`provider/apache/RocketMQMessageProvider.java:87`),
   - a body that is not valid UTF-8 is replaced by the Base64 text of at most 
48 KiB (`displayBody`, `:836-861`),
   - the user-property map is capped at 64 entries and 1024 code points per 
value (`MessagePropertyDisplay.limitProperties`, 
`common/util/MessagePropertyDisplay.java:35,43,63`).
   
   Chain: `MessageRedeliveryToolHandler.execute` 
(`ops/ai/tool/handler/message/MessageRedeliveryToolHandler.java:63,76`) → 
`MetadataService.redeliverMessage(String,String,String,String,String)` 
(`instance/topic/MetadataService.java:293-298`) → `findMessageForRedelivery` 
(`:342-350`) → `MessageService.queryMessages` → 
`RocketMQMessageProvider.toRecordVO` 
(`provider/apache/RocketMQMessageProvider.java:811-832`, display caps applied 
at `:813,:824`) → `MetadataService.redeliverMessage(..., MessageRecordVO, ...)` 
(`:300-315`) publishes `original.getBody()`.
   
   The tool's own plan text promises the opposite: "Publishes a new message 
containing the source payload and properties" 
(`MessageRedeliveryToolHandler.java:43-45`), and `findMessageForRedelivery` is 
documented as loading "the exact source message" (`MetadataService.java:341`). 
The sibling DLQ path does use the raw bytes 
(`provider/apache/RocketMQDLQProvider.java:583`).
   
   Trigger (deterministic): redeliver any message whose body is larger than 64 
KiB, or whose body is not valid UTF-8 text, or that carries more than 64 user 
properties. The copy is published without any warning that it is a partial 
copy; nothing in the response (`SendMessageVO`) indicates the payload was 
rewritten.
   
   ### Evidence
   
   - 
`server/src/main/java/org/apache/rocketmq/studio/instance/topic/MetadataService.java:293-298`
 — the five-argument entry point loads the source through 
`findMessageForRedelivery` and passes that VO straight to the publisher.
   - 
`server/src/main/java/org/apache/rocketmq/studio/instance/topic/MetadataService.java:342-350`
 — `findMessageForRedelivery` is `messageService.queryMessages(...)`, i.e. the 
same display projection the message explorer renders.
   - 
`server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProvider.java:811-832`
 — `toRecordVO` builds `body(displayBody.value())`, `bodyEncoding(...)`, 
`bodyTruncated(...)`, `properties(displayProperties)`, 
`propertiesTruncated(...)`; `displayBody` (`:836-861`) truncates at 64 KiB / 48 
KiB and Base64-encodes non-UTF-8 bodies.
   - 
`server/src/main/java/org/apache/rocketmq/studio/common/util/MessagePropertyDisplay.java:35,43,63`
 — the property caps and their stated purpose ("rendering limits for message 
property maps").
   - Regression test added with this report (fails on 7ce9a682): a source VO 
with `bodyTruncated=true`, one with `bodyEncoding="BASE64"`, and one with 
`propertiesTruncated=true` are each published today 
(`verify(provider).sendMessage(...)` succeeds, body = the truncated string); 
the tests expect a refusal instead.
   
   ### Impact
   
   A redelivered message is a corrupted copy: for a 100 KiB body only the first 
65536 bytes are republished, a binary payload is republished as Base64 text 
(different bytes, different semantics for the consumer), and properties past 
the display cap disappear. The operation reports success, so the loss is only 
discoverable downstream — after the consumer has already processed a message 
that is not the one it was sent.
   
   ### Expected behavior
   
   `rmq.message.redelivery` either publishes the exact stored payload, or 
refuses the operation with a clear 4xx when the source cannot be reproduced 
exactly (truncated body, Base64-projected binary body, capped property map) — 
it must not publish a silently rewritten copy. The refusal path keeps 
`MessageRecordVO`'s display contract unchanged for the message explorer, which 
is where the caps belong.
   
   ### Related work
   
   - #4606 (merged, 5b761df8) — the AI conversation platform that introduced 
`rmq.message.redelivery` and its plan text.
   - 
`server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQDLQProvider.java:583`
 — the DLQ resend path already uses the raw `deadLetter.getBody()`, which is 
the behavior this path was expected to match.
   - The display caps are shared deliberately (`MessagePropertyDisplay` 
javadoc): the defect is that a *display* projection is used as a *write* 
payload, not that the caps exist.
   
   #
   
   ## PR
   
   Fix: TBD.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to