{"record":{"id":"fa5b3f09adb6f93d","repo":"tracel-ai/burn","slug":"failed-to-broadcast-rhs","errorCode":null,"errorMessage":"Failed to broadcast rhs","messagePattern":"Failed to broadcast rhs","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/burn-ndarray/src/ops/base.rs","lineNumber":840,"sourceCode":"            broadcast_shape[ndims - 1 - i] = lhs_dim;\n        } else {\n            panic!(\n                \"Incompatible shapes for broadcasting: {:?} and {:?}\",\n                lhs_shape, rhs_shape\n            );\n        }\n    }\n\n    // Create IxDyn from broadcast shape\n    let broadcast_dim = ndarray::IxDyn(&broadcast_shape);\n\n    // Broadcast both arrays\n    let lhs_broadcast = lhs\n        .broadcast(broadcast_dim.clone())\n        .expect(\"Failed to broadcast lhs\");\n    let rhs_broadcast = rhs\n        .broadcast(broadcast_dim)\n        .expect(\"Failed to broadcast rhs\");\n\n    (lhs_broadcast, rhs_broadcast)\n}\n\n/// The mean of zero elements, which is `0 / 0`.\n///\n/// `NaN` for a float, matching numpy and torch. Integers have no such value, so an integer mean of\n/// nothing is rejected rather than silently reported as some other number.\npub(crate) fn empty_mean<E: NdArrayElement>() -> E {\n    assert!(\n        E::dtype().is_float(),\n        \"Cannot compute mean of empty tensor for the integer type {:?}\",\n        E::dtype()\n    );\n    0.elem::<E>() / 0.elem::<E>()\n}\n\nimpl<E> NdArrayMathOps<E>","sourceCodeStart":822,"sourceCodeEnd":858,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-ndarray/src/ops/base.rs#L822-L858","documentation":"Same as the lhs broadcast failure, but for the right-hand operand: broadcast_for_binary_ops broadcasts rhs to the merged shape and panics if ndarray's broadcast() fails. Indicates the two operands of a comparison/remainder op are not broadcast-compatible.","triggerScenarios":"Calling remainder, equal, greater, greater_equal, lower_equal, or lower where the rhs tensor's shape cannot be broadcast to the computed broadcast_shape (dims mismatch and rhs dim != 1, or rhs rank exceeds available dims).","commonSituations":"Comparing model output [B, 10] against labels [B] or [B, 8]; scalar-like tensor created with wrong shape; pipeline refactors that changed one operand's rank.","solutions":["Make rhs broadcastable to lhs: matching trailing dims or rhs dims of 1; add unsqueeze for rank alignment","Reshape rhs explicitly before the op (e.g. [B] -> [B, 1])","Fix the upstream producer of rhs so its shape matches expectations"],"exampleFix":"// before\nlet labels: Tensor<NdArray, 1> = ...; // [B]\nlet eq = logits.greater_equal(labels); // [B, C] vs [B] fine only if C aligned; e.g. [B,C] vs [C'] panics\n// after\nlet labels2 = labels.unsqueeze(); // [B, 1]\nlet eq = logits.greater_equal(labels2);","handlingStrategy":"validation","validationCode":"// ensure rhs broadcasts to lhs' shape before comparison ops\nfn align_rhs(lhs_dims: &[usize], rhs: Tensor<NdArray<F>, 1>) -> Tensor<NdArray<F>, 2> {\n    // e.g. [B] -> [B, 1] to compare against [B, C]\n    rhs.unsqueeze()\n}\n// check trailing dims match or one is 1 before calling op","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Reshape label/other-side tensors to rank matching the main operand (unsqueeze dim of 1)","Keep scalar-ish operands as rank-1 with size 1 or matching trailing dims","Fix upstream producers so comparison operands share a known shape contract","Add unit tests that run comparisons with representative shapes"],"tags":["rust","ndarray","panic","broadcast","elementwise","shape"],"backgroundTag":"tensor-broadcast-shape-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"}