{"record":{"id":"719f0f8002ea3e6c","repo":"screenpipe/screenpipe","slug":"serialize-eval-request","errorCode":null,"errorMessage":"serialize eval request","messagePattern":"serialize eval request","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/screenpipe-connect/src/connections/browser/bridge.rs","lineNumber":178,"sourceCode":"    ) -> Result<EvalResult, EvalError> {\n        // Snapshot the transport — if we lose it after this point, the send\n        // will fail and we'll report it cleanly.\n        let transport = {\n            let guard = self.transport.read().await;\n            guard.as_ref().cloned().ok_or(EvalError::NotConnected)?\n        };\n\n        let id = uuid::Uuid::new_v4().to_string();\n        let (tx, rx) = oneshot::channel();\n        self.pending.lock().await.insert(id.clone(), tx);\n\n        let frame = serde_json::to_string(&WsEvalRequest {\n            id: &id,\n            action: \"eval\",\n            code,\n            url,\n        })\n        .expect(\"serialize eval request\");\n\n        if let Err(e) = transport.send_text(frame).await {\n            self.pending.lock().await.remove(&id);\n            // The transport is dead — clear it so /status reflects reality.\n            self.detach_transport(&transport).await;\n            return Err(EvalError::SendFailed(e));\n        }\n\n        match tokio::time::timeout(timeout, rx).await {\n            Ok(Ok(result)) => Ok(result),\n            Ok(Err(_)) => Err(EvalError::Disconnected),\n            Err(_) => {\n                self.pending.lock().await.remove(&id);\n                Err(EvalError::Timeout(timeout.as_secs()))\n            }\n        }\n    }\n","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/screenpipe/screenpipe/blob/4ebf712990fee17eeaf904dacf749b6e96ac9bf3/crates/screenpipe-connect/src/connections/browser/bridge.rs#L160-L196","documentation":"Bridge::eval serializes a WsEvalRequest to JSON before sending over the WebSocket transport and unwraps with .expect(\"serialize eval request\"). serde_json::to_string can only fail if the value contains non-string map keys or an IO error on the internal writer — impossible for this struct with &str fields, so it is effectively an infallible invariant assertion that panics only if the struct gains a non-serializable field.","triggerScenarios":"adding a field to WsEvalRequest whose serialization can fail (e.g. a map with non-string keys) or replacing serde_json with a fallible serializer; a code change introducing a poison value into `code`/`url` is not a real trigger since they are &str.","commonSituations":"schema evolution of the bridge protocol; swapping the JSON library for one with stricter constraints.","solutions":["propagate serialization as an EvalError::Serialize variant instead of panicking","keep WsEvalRequest fields as strings/primitives that serde_json can never fail on","add a unit test serializing the request so regressions surface in CI"],"exampleFix":"// before\nserde_json::to_string(&WsEvalRequest { ... }).expect(\"serialize eval request\");\n// after\nlet frame = serde_json::to_string(&WsEvalRequest { ... })\n    .map_err(|e| EvalError::Serialize(e))?;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let frame = serde_json::to_string(&request)\n    .map_err(|e| EvalError::Serialize(e))?;","preventionTips":["keep wire structs to String/&str/primitive fields so serde_json serialization is infallible","add a serialization unit test for WsEvalRequest","return a dedicated Serialize error variant instead of .expect in async request paths"],"tags":["serde","json","websocket","panic"],"backgroundTag":"serde-serialization-panic","analyzedSha":"4ebf712990fee17eeaf904dacf749b6e96ac9bf3","analyzedAt":"2026-09-01T23:33:43.065Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}