{"record":{"id":"bfe8113a5bd12aec","repo":"tracel-ai/burn","slug":"invalid-response-type-for-readtensor","errorCode":null,"errorMessage":"Invalid response type for ReadTensor","messagePattern":"Invalid response type for ReadTensor","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/burn-remote/src/client/runner.rs","lineNumber":47,"sourceCode":"        self.handle.submit(move |s| s.register_op(stream_id, op));\n    }\n\n    fn read_tensor_async(\n        &self,\n        tensor: burn_ir::TensorIr,\n    ) -> DynFut<Result<TensorData, ExecutionError>> {\n        // Issue the request synchronously so ordering is preserved relative to subsequent\n        // submissions; the returned future just awaits the server's response.\n        let stream_id = StreamId::current();\n        let rx = self\n            .handle\n            .submit_blocking(move |s| s.read_tensor(stream_id, tensor))\n            .expect(\"Service call failed\");\n\n        Box::pin(async move {\n            match rx.await {\n                Ok(TaskResponseContent::ReadTensor(res)) => res,\n                Ok(_) => panic!(\"Invalid response type for ReadTensor\"),\n                Err(e) => Err(ExecutionError::Generic {\n                    reason: format!(\"Failed to read tensor: {e:?}\"),\n                    backtrace: BackTrace::capture(),\n                }),\n            }\n        })\n    }\n\n    fn register_tensor_data(&self, data: TensorData) -> RouterTensor<Self> {\n        let shape = data.shape.clone();\n        let dtype = data.dtype;\n        let id = service::new_tensor_id();\n\n        // Fire-and-forget: the outgoing batch flushes itself once buffered data bytes (or the task\n        // count) cross their threshold — see `OutgoingBatch` — so no explicit flush is needed here.\n        let stream_id = StreamId::current();\n        self.handle\n            .submit(move |s| s.register_tensor(stream_id, id, data));","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-remote/src/client/runner.rs#L29-L65","documentation":"In burn-remote, `read_tensor_async` submits a tensor-read request to the remote service and awaits a response on a oneshot channel. The protocol contract is that the reply carries `TaskResponseContent::ReadTensor`; if the server/channel delivers any other task-response variant, the client panics with this message. It indicates a client/server protocol desynchronization or a mismatched response routing, not a tensor data error (genuine transport failures come back as `ExecutionError::Generic` on the Err branch).","triggerScenarios":"Calling `.into_data()`/`to_data()` (which routes through RouterClient::read_tensor_async) on a tensor owned by a RemoteClient when the oneshot `rx` resolves with a TaskResponseContent variant other than ReadTensor — i.e. the response queued for this stream id belongs to a different task.","commonSituations":"Client/server version skew where response variants were reordered or remapped between burn versions; running mismatched burn-remote client and server binaries; stream id reuse/collision after sync; custom or forked server code replying with the wrong TaskResponseContent.","solutions":["Rebuild and redeploy client and server from the same burn version so the TaskResponseContent wire protocol matches.","Verify no custom/forked service code routes responses to the wrong stream id for read_tensor requests.","Update to the latest burn release; if it persists, file a bug with a minimal repro (remote backend, read a tensor after a couple of ops)."],"exampleFix":"// before: client 0.x talking to server 0.y (protocol skew) -> panic\n// after: pin the same burn version on both sides\n# Cargo.toml (client and server)\n[dependencies]\nburn = { version = \"=0.18.0\", features = [\"remote\"] }","handlingStrategy":"try-catch","validationCode":"// ensure client and server report the same burn version before exchanging tensors\nassert_eq!(client.protocol_version(), server.protocol_version());","typeGuard":"match response {\n    TaskResponseContent::ReadTensor(res) => Ok(res),\n    other => Err(format!(\"unexpected response variant: {other:?}\")),\n}","tryCatchPattern":"// panic! cannot be caught in stable Rust without catch_unwind\nlet result = std::panic::catch_unwind(AssertUnwindSafe(|| tensor.into_data()));\nmatch result {\n    Ok(data) => { /* use data */ }\n    Err(_) => { /* reconnect with matching versions */ }\n}","preventionTips":["Pin identical burn versions in client and server Cargo.tomls","Add a version/handshake check when opening the remote connection","Avoid custom forks of the remote service protocol"],"tags":["rust","panic","remote","protocol-mismatch"],"backgroundTag":"rpc-response-type-mismatch","analyzedSha":"d16f7ba2ed0d41408189384044cc886fb4c8f957","analyzedAt":"2026-09-05T13:19:14.260Z","contentChangedAt":"2026-09-05T13:19:14.260Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}