#1474·RD-Agent

Bug: debug /receive endpoint returns 500 for valid list payloads

Author: yifanxiong272Created Sep 2, 2026Updated Sep 5, 2026
Labelsbug

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:

python
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"].

python
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:

json
[
  {
    "id": "trace-1",
    "msg": {
      "tag": "A"
    }
  },
  {
    "id": "trace-1",
    "msg": {
      "tag": "B"
    }
  }
]

returns:

json
{
  "error": "Internal Server Error"
}

instead of appending both messages and returning success.

To Reproduce

Steps to reproduce the behavior:

  1. Check out RD-Agent main at commit:
2c878f9d2453dced35061165786d1f31bbff0ab6
  1. Install RD-Agent from source and install pytest:
bash
python -m pip install -e .
python -m pip install pytest
  1. Create test/test_debug_receive_msgs_list_payload.py:
python
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,
            },
        },
    ]
  1. Run:
bash
python -m pytest \
  test/test_debug_receive_msgs_list_payload.py \
  -q
  1. Observe that the test fails because the endpoint returns HTTP 500.

Expected Behavior

A valid JSON list payload should follow the existing list branch:

python
if isinstance(data, list):
    for d in data:
        msgs_for_frontend[d["id"]].append(d["msg"])

The endpoint should return:

json
{
  "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:

python
@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"}), 200

The 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:

python
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_frontend on rejected payloads.

Targeted issue and pull-request searches found no existing report for this debug /receive list-payload root.