Skip to content

:keep-alive messages causing a2a-sdk (Python) client to crash #99

@xenv

Description

@xenv

What happened?

The go-sdk periodically sends :keep-alive messages. While this is valid for SSE, it causes many a2a-python clients to crash because they cannot parse it correctly.

For example, when using the client from https://github.com/a2aproject/a2a-samples/tree/main/samples/python/hosts/cli to access a long-running streaming request from a go-sdk server, it will crash. I printed the message that causes the parsing error, and it's an empty string (''). I believe this corresponds to the :keep-alive message.

Relevant log output

Traceback (most recent call last):
  File "/root/a2a-samples/samples/python/.venv/lib/python3.13/site-packages/a2a/client/transports/jsonrpc.py", line 167, in send_message_streaming
    json.loads(sse.data)
    ~~~~~~~~~~^^^^^^^^^^
  File "/root/.local/share/uv/python/cpython-3.13.9-linux-x86_64-gnu/lib/python3.13/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ~~~~~~~~~~~~~~~~~~~~~~~^^^
  File "/root/.local/share/uv/python/cpython-3.13.9-linux-x86_64-gnu/lib/python3.13/json/decoder.py", line 345, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/uv/python/cpython-3.13.9-linux-x86_64-gnu/lib/python3.13/json/decoder.py", line 363, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/root/a2a-samples/samples/python/hosts/cli/__main__.py", line 297, in <module>
    asyncio.run(cli())
                ~~~^^
  File "/root/a2a-samples/samples/python/.venv/lib/python3.13/site-packages/asyncclick/core.py", line 1515, in __call__
    return anyio.run(self._main, main, args, kwargs, backend=_anyio_backend)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/a2a-samples/samples/python/.venv/lib/python3.13/site-packages/anyio/_core/_eventloop.py", line 74, in run
    return async_backend.run(func, args, {}, backend_options)
           ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/a2a-samples/samples/python/.venv/lib/python3.13/site-packages/anyio/_backends/_asyncio.py", line 2316, in run
    return runner.run(wrapper())
           ~~~~~~~~~~^^^^^^^^^^^
  File "/root/.local/share/uv/python/cpython-3.13.9-linux-x86_64-gnu/lib/python3.13/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/root/.local/share/uv/python/cpython-3.13.9-linux-x86_64-gnu/lib/python3.13/asyncio/base_events.py", line 725, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "/root/a2a-samples/samples/python/.venv/lib/python3.13/site-packages/anyio/_backends/_asyncio.py", line 2304, in wrapper
    return await func(*args)
           ^^^^^^^^^^^^^^^^^
  File "/root/a2a-samples/samples/python/.venv/lib/python3.13/site-packages/asyncclick/core.py", line 1526, in _main
    return await main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/a2a-samples/samples/python/.venv/lib/python3.13/site-packages/asyncclick/core.py", line 1425, in main
    rv = await self.invoke(ctx)
         ^^^^^^^^^^^^^^^^^^^^^^
  File "/root/a2a-samples/samples/python/.venv/lib/python3.13/site-packages/asyncclick/core.py", line 1288, in invoke
    return await ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/a2a-samples/samples/python/.venv/lib/python3.13/site-packages/asyncclick/core.py", line 855, in invoke
    rv = await rv
         ^^^^^^^^
  File "/root/a2a-samples/samples/python/hosts/cli/__main__.py", line 110, in cli
    continue_loop, _, task_id = await completeTask(
                                ^^^^^^^^^^^^^^^^^^^
    ...<7 lines>...
    )
    ^
  File "/root/a2a-samples/samples/python/hosts/cli/__main__.py", line 199, in completeTask
    async for result in response_stream:
    ...<20 lines>...
        print(f'stream event => {event.model_dump_json(exclude_none=True)}')
  File "/root/a2a-samples/samples/python/.venv/lib/python3.13/site-packages/a2a/client/legacy.py", line 125, in send_message_streaming
    async for result in self._transport.send_message_streaming(
    ...<6 lines>...
        )
  File "/root/a2a-samples/samples/python/.venv/lib/python3.13/site-packages/a2a/client/transports/jsonrpc.py", line 177, in send_message_streaming
    raise A2AClientJSONError(f"{str(e)} - Raw data: {sse.data!r}") from e
a2a.client.errors.A2AClientJSONError: JSON Error: Expecting value: line 1 column 1 (char 0) - Raw data: ''

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions