@@ -217,26 +217,43 @@ async fn send_jsonrpc_request<E: EventEmitter>(
217
217
} ,
218
218
) ;
219
219
220
- // Read response
220
+ // Read response - keep reading lines until we get valid JSON
221
221
let mut line = String :: new ( ) ;
222
- reader
223
- . read_line ( & mut line)
224
- . await
225
- . map_err ( |e| BackendError :: SessionInitFailed ( format ! ( "Failed to read response: {e}" ) ) ) ?;
222
+ let trimmed_line = loop {
223
+ line. clear ( ) ;
224
+ reader
225
+ . read_line ( & mut line)
226
+ . await
227
+ . map_err ( |e| BackendError :: SessionInitFailed ( format ! ( "Failed to read response: {e}" ) ) ) ?;
226
228
227
- let trimmed_line = line. trim ( ) ;
228
- println ! ( "🔍 RAW OUTPUT FROM GEMINI CLI: {trimmed_line }" ) ;
229
- let _ = rpc_logger. log_rpc ( line . trim ( ) ) ;
229
+ let trimmed = line. trim ( ) ;
230
+ println ! ( "🔍 RAW OUTPUT FROM GEMINI CLI: {trimmed }" ) ;
231
+ let _ = rpc_logger. log_rpc ( trimmed ) ;
230
232
231
- let _ = emitter. emit (
232
- & format ! ( "cli-io-{session_id}" ) ,
233
- CliIoPayload {
234
- io_type : CliIoType :: Output ,
235
- data : line. trim ( ) . to_string ( ) ,
236
- } ,
237
- ) ;
233
+ let _ = emitter. emit (
234
+ & format ! ( "cli-io-{session_id}" ) ,
235
+ CliIoPayload {
236
+ io_type : CliIoType :: Output ,
237
+ data : trimmed. to_string ( ) ,
238
+ } ,
239
+ ) ;
240
+
241
+ // Skip non-JSON lines like "Data collection is disabled."
242
+ if trimmed. is_empty ( ) || ( !trimmed. starts_with ( '{' ) && !trimmed. starts_with ( '[' ) ) {
243
+ println ! ( "🔍 Skipping non-JSON line: {trimmed}" ) ;
244
+ continue ;
245
+ }
238
246
239
- let response = serde_json:: from_str :: < JsonRpcResponse > ( & line)
247
+ // Try to parse as JSON - if it fails, continue reading
248
+ if serde_json:: from_str :: < serde_json:: Value > ( trimmed) . is_ok ( ) {
249
+ break trimmed. to_string ( ) ;
250
+ } else {
251
+ println ! ( "🔍 Line is not valid JSON, continuing: {trimmed}" ) ;
252
+ continue ;
253
+ }
254
+ } ;
255
+
256
+ let response = serde_json:: from_str :: < JsonRpcResponse > ( & trimmed_line)
240
257
. map_err ( |e| BackendError :: SessionInitFailed ( format ! ( "Failed to parse response: {e}" ) ) ) ?;
241
258
242
259
if let Some ( error) = & response. error {
@@ -1679,6 +1696,42 @@ mod tests {
1679
1696
assert_eq ! ( guard. get( "thread-test" ) . unwrap( ) . pid, Some ( 999 ) ) ;
1680
1697
}
1681
1698
1699
+ #[ test]
1700
+ fn test_skip_non_json_lines ( ) {
1701
+ // Test that we correctly identify non-JSON lines that should be skipped
1702
+ let non_json_lines = vec ! [
1703
+ "Data collection is disabled." ,
1704
+ "" ,
1705
+ "Warning: Something happened" ,
1706
+ "Loading..." ,
1707
+ "debug: info" ,
1708
+ ] ;
1709
+
1710
+ for line in non_json_lines {
1711
+ // These should be identified as non-JSON and skipped
1712
+ let is_json_candidate = !line. trim ( ) . is_empty ( ) &&
1713
+ ( line. trim ( ) . starts_with ( '{' ) || line. trim ( ) . starts_with ( '[' ) ) ;
1714
+ assert ! ( !is_json_candidate, "Line '{}' should be skipped as non-JSON" , line) ;
1715
+ }
1716
+
1717
+ // Test valid JSON lines
1718
+ let json_lines = vec ! [
1719
+ r#"{"jsonrpc": "2.0", "id": 1, "result": {}}"# ,
1720
+ r#"[{"type": "test"}]"# ,
1721
+ r#"{"method": "test"}"# ,
1722
+ ] ;
1723
+
1724
+ for line in json_lines {
1725
+ let is_json_candidate = !line. trim ( ) . is_empty ( ) &&
1726
+ ( line. trim ( ) . starts_with ( '{' ) || line. trim ( ) . starts_with ( '[' ) ) ;
1727
+ assert ! ( is_json_candidate, "Line '{}' should be considered as potential JSON" , line) ;
1728
+
1729
+ // Verify it's actually parseable JSON
1730
+ assert ! ( serde_json:: from_str:: <serde_json:: Value >( line. trim( ) ) . is_ok( ) ,
1731
+ "Line '{}' should be valid JSON" , line) ;
1732
+ }
1733
+ }
1734
+
1682
1735
#[ test]
1683
1736
fn test_session_manager_stress_add_remove ( ) {
1684
1737
let manager = SessionManager :: new ( ) ;
0 commit comments