Bug: debug /receive endpoint returns 500 for valid list payloads
Bug Description
The debug server's /receive endpoint in rdagent/log/server/debug_app.py has an explicit branch for receiving a JSON list of message objects:
if isinstance(data, list):
for d in data:
msgs_for_frontend[d["id"]].append(d["msg"])However, before reaching that list branch, the handler unconditionally dereferences the parsed JSON payload as a dictionary via data["msg"]["tag"].
data = request.get_json()
app.logger.info(data["msg"]["tag"])As a result, a valid list payload is treated like a dictionary during logging, raises an exception, and returns HTTP 500 before the list branch can run.
For example, this payload:
[
{
"id": "trace-1",
"msg": {
"tag": "A"
}
},
{
"id": "trace-1",
"msg": {
"tag": "B"
}
}
]returns:
{
"error": "Internal Server Error"
}instead of appending both messages and returning success.
To Reproduce
Steps to reproduce the behavior:
- Check out RD-Agent
mainat commit:
2c878f9d2453dced35061165786d1f31bbff0ab6- Install RD-Agent from source and install pytest:
python -m pip install -e .
python -m pip install pytest- Create
test/test_debug_receive_msgs_list_payload.py:
from rdagent.log.server import debug_app
def test_receive_msgs_accepts_list_payload():
app = debug_app.app
client = app.test_client()
debug_app.msgs_for_frontend.clear()
response = client.post(
"/receive",
json=[
{
"id": "trace-1",
"msg": {
"tag": "A",
"content": {
"value": 1,
},
},
},
{
"id": "trace-1",
"msg": {
"tag": "B",
"content": {
"value": 2,
},
},
},
],
)
assert response.status_code == 200
assert response.get_json() == {
"status": "success",
}
assert debug_app.msgs_for_frontend["trace-1"] == [
{
"tag": "A",
"content": {
"value": 1,
},
},
{
"tag": "B",
"content": {
"value": 2,
},
},
]- Run:
python -m pytest \
test/test_debug_receive_msgs_list_payload.py \
-q- Observe that the test fails because the endpoint returns HTTP 500.
Expected Behavior
A valid JSON list payload should follow the existing list branch:
if isinstance(data, list):
for d in data:
msgs_for_frontend[d["id"]].append(d["msg"])The endpoint should return:
{
"status": "success"
}with HTTP 200, and each message should be appended under its corresponding id in order.
Screenshot
Not applicable. This is a deterministic Flask test-client reproduction.
Environment
Note: Users can run rdagent collect_info to get system information and paste it directly here.
- Name of current operating system: macOS
- Processor architecture: arm64
- System, version, and hardware information: macOS 15.7.3, arm64
- Version number of the system: 15.7.3
- Python version: 3.13.2
- Container ID: Not applicable
- Container Name: Not applicable
- Container Status: Not applicable
- Image ID used by the container: Not applicable
- Image tag used by the container: Not applicable
- Container port mapping: Not applicable
- Container Label: Not applicable
- Startup Commands: Not applicable
- RD-Agent version:
main@2c878f9d2453dced35061165786d1f31bbff0ab6 - Package version: Source checkout
Additional Notes
The current implementation is:
@app.route("/receive", methods=["POST"])
def receive_msgs():
try:
data = request.get_json()
app.logger.info(data["msg"]["tag"])
if not data:
return jsonify({"error": "No JSON data received"}), 400
except Exception as e:
return jsonify({"error": "Internal Server Error"}), 500
if isinstance(data, list):
for d in data:
msgs_for_frontend[d["id"]].append(d["msg"])
else:
msgs_for_frontend[data["id"]].append(data["msg"])
return jsonify({"status": "success"}), 200The logging line assumes the payload is a dictionary, but the later code explicitly supports lists. Moving the log after shape validation, or logging list payloads separately, would allow the list branch to run.
A possible fix is to validate data before dereferencing it as a dictionary:
data = request.get_json()
if not data:
return jsonify({"error": "No JSON data received"}), 400
if isinstance(data, list):
for d in data:
app.logger.info(d["msg"]["tag"])
msgs_for_frontend[d["id"]].append(d["msg"])
else:
app.logger.info(data["msg"]["tag"])
msgs_for_frontend[data["id"]].append(data["msg"])Regression coverage should include:
- a valid single-message dictionary payload;
- a valid list payload with multiple messages;
- preservation of list message order;
- missing JSON returning 400 instead of 500;
- malformed payloads returning a client error rather than being accepted;
- no mutation of
msgs_for_frontendon rejected payloads.
Targeted issue and pull-request searches found no existing report for this debug /receive list-payload root.
Source: microsoft/RD-Agent