screenpipe/screenpipe · warning

serialize eval request

Error message

serialize eval request

What it means

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.

Source

Thrown at crates/screenpipe-connect/src/connections/browser/bridge.rs:178

    ) -> Result<EvalResult, EvalError> {
        // Snapshot the transport — if we lose it after this point, the send
        // will fail and we'll report it cleanly.
        let transport = {
            let guard = self.transport.read().await;
            guard.as_ref().cloned().ok_or(EvalError::NotConnected)?
        };

        let id = uuid::Uuid::new_v4().to_string();
        let (tx, rx) = oneshot::channel();
        self.pending.lock().await.insert(id.clone(), tx);

        let frame = serde_json::to_string(&WsEvalRequest {
            id: &id,
            action: "eval",
            code,
            url,
        })
        .expect("serialize eval request");

        if let Err(e) = transport.send_text(frame).await {
            self.pending.lock().await.remove(&id);
            // The transport is dead — clear it so /status reflects reality.
            self.detach_transport(&transport).await;
            return Err(EvalError::SendFailed(e));
        }

        match tokio::time::timeout(timeout, rx).await {
            Ok(Ok(result)) => Ok(result),
            Ok(Err(_)) => Err(EvalError::Disconnected),
            Err(_) => {
                self.pending.lock().await.remove(&id);
                Err(EvalError::Timeout(timeout.as_secs()))
            }
        }
    }

View on GitHub (pinned to 4ebf712990)

Solutions

  1. propagate serialization as an EvalError::Serialize variant instead of panicking
  2. keep WsEvalRequest fields as strings/primitives that serde_json can never fail on
  3. add a unit test serializing the request so regressions surface in CI

Example fix

// before
serde_json::to_string(&WsEvalRequest { ... }).expect("serialize eval request");
// after
let frame = serde_json::to_string(&WsEvalRequest { ... })
    .map_err(|e| EvalError::Serialize(e))?;
Defensive patterns

Strategy: try-catch

Try / catch

let frame = serde_json::to_string(&request)
    .map_err(|e| EvalError::Serialize(e))?;

Prevention

When it happens

Trigger: 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.

Common situations: schema evolution of the bridge protocol; swapping the JSON library for one with stricter constraints.

Related errors


AI-assisted analysis of screenpipe/screenpipe@4ebf712990 (2026-09-01). Data as JSON: /api/errors/719f0f8002ea3e6c. Report an issue: GitHub.