@@ -121,3 +121,31 @@ async def test_receive_loop_does_not_swallow_unrelated_reader_error() -> None:
121121 with pytest .raises (ValueError , match = "reader failed" ):
122122 await conn ._receive_loop ()
123123 await conn .close ()
124+
125+
126+ @pytest .mark .asyncio
127+ async def test_receive_loop_ignores_frames_that_are_not_json_objects () -> None :
128+ """A line can be valid JSON and still not be a JSON-RPC message.
129+
130+ Unparsable input is already skipped by ``NdjsonTransport.receive``. These frames parse
131+ fine but are not objects: an array, a number, a string and ``null``. Without the object
132+ guard they reach ``Connection._process_message`` -- which calls ``message.get(...)`` --
133+ or, for ``null``, are read as the transport's EOF signal. Either way the connection dies
134+ and the valid frame queued behind them is never handled.
135+ """
136+ conn , reader = _make_connection ()
137+ processed : list [str ] = []
138+
139+ def tracking_process (message : dict [str , Any ]) -> None :
140+ processed .append (message ["method" ])
141+
142+ conn ._process_message = tracking_process # type: ignore[method-assign]
143+ non_objects = b"\n " .join ([b"[]" , b"123" , b'"x"' , b"null" , b"" , b"not json at all" ])
144+ survivor = {"jsonrpc" : "2.0" , "method" : "survivor" }
145+ reader .feed_data (non_objects + b"\n " + json .dumps (survivor ).encode () + b"\n " )
146+ reader .feed_eof ()
147+
148+ await conn ._receive_loop ()
149+ await conn .close ()
150+
151+ assert processed == ["survivor" ]
0 commit comments