{"record":{"id":"b046c8ae67cf1e7e","repo":"screenpipe/screenpipe","slug":"boxes-cat","errorCode":null,"errorMessage":"boxes cat","messagePattern":"boxes cat","errorType":"exception","errorClass":"Error::Inference","httpStatus":null,"severity":"error","filePath":"crates/screenpipe-rfdetr-mlx/src/decoder/mod.rs","lineNumber":689,"sourceCode":"        // Final LN.\n        output = ln(&output, &self.final_norm_w, &self.final_norm_b)?;\n\n        // Final heads.\n        let logits = linear(&output, &self.class_head_w, &self.class_head_b)?;\n        let bbox_delta = self.bbox_head.forward(&output)?;\n        // Final bbox refinement vs. refpoint_embed (cxcywh).\n        let rb_parts = ops::split_sections(&refpoint_embed, &[2], -1).map_err(err(\"rb split\"))?;\n        let bd_parts = ops::split_sections(&bbox_delta, &[2], -1).map_err(err(\"bd split\"))?;\n        let final_cxcy = bd_parts[0]\n            .multiply(&rb_parts[1])\n            .map_err(err(\"final cxcy mul\"))?\n            .add(&rb_parts[0])\n            .map_err(err(\"final cxcy add\"))?;\n        let final_wh = exp_(&bd_parts[1])?\n            .multiply(&rb_parts[1])\n            .map_err(err(\"final wh mul\"))?;\n        let boxes =\n            ops::concatenate_axis(&[&final_cxcy, &final_wh], -1).map_err(err(\"boxes cat\"))?;\n\n        Ok((boxes, logits))\n    }\n\n    /// Gather slices of `x` along axis 1 by integer indices `idx (B, K)`.\n    /// Result: `(B, K, last_dim)`.\n    fn gather(&self, x: &Array, idx: &Array, last_dim: i32) -> Result<Array> {\n        let s = x.shape();\n        let (b, _len, _c) = (s[0], s[1], s[2]);\n        let k = idx.shape()[1];\n        // Broadcast idx (B, K) → (B, K, last_dim) so take_along_axis can gather.\n        let idx_3d = idx.reshape(&[b, k, 1]).map_err(err(\"idx 3d\"))?;\n        let idx_bc = ops::broadcast_to(&idx_3d, &[b, k, last_dim]).map_err(err(\"idx bc\"))?;\n        x.take_along_axis(&idx_bc, 1).map_err(err(\"gather\"))\n    }\n}\n\n#[allow(dead_code)]","sourceCodeStart":671,"sourceCodeEnd":707,"githubUrl":"https://github.com/screenpipe/screenpipe/blob/4ebf712990fee17eeaf904dacf749b6e96ac9bf3/crates/screenpipe-rfdetr-mlx/src/decoder/mod.rs#L671-L707","documentation":"Raised when concatenating the final centers `final_cxcy` and sizes `final_wh` along the last axis to form the (B, 300, 4) output boxes. mlx `concatenate_axis` requires all inputs to have identical shapes on every axis except the concatenation axis. The library throws it when the two halves differ in batch, query count, or (if a prior split misbehaved) when the halves aren't each 2 wide.","triggerScenarios":"Downstream mismatch after the mul/add steps — e.g. final_cxcy ended up (B, 300, 2) but final_wh is (B, 1, 2) or (B, 299, 2) due to an upstream broadcast/slice bug, or an extra axis survived a reshape.","commonSituations":"Custom head modifications changing channel widths; partially failing earlier ops retried with different shapes; porting changes from the ONNX exporter where halves were computed with different slicing.","solutions":["Check `final_cxcy.shape()` and `final_wh.shape()` are identical except allowing last-axis concat; assert both are (B, 300, 2).","Fix the producing op (split/mul/add) rather than patching here — the mismatch originates earlier.","If one operand is (B, 300, 1, 2), reshape/squeeze to (B, 300, 2) before concatenating.","Compare against the ONNX exporter's expected (dets) output shape (B, 300, 4) as a sanity check."],"exampleFix":"// before\nlet boxes = ops::concatenate_axis(&[&final_cxcy, &final_wh], -1).map_err(err(\"boxes cat\"))?;\n// after\nassert_eq!(final_cxcy.shape(), final_wh.shape(), \"cxcy/wh halves must match\");\nlet final_wh = final_wh.reshape(final_cxcy.shape())?; // drop stray axis if any\nlet boxes = ops::concatenate_axis(&[&final_cxcy, &final_wh], -1).map_err(err(\"boxes cat\"))?;","handlingStrategy":"validation","validationCode":"// Before concatenation\nlet c = final_cxcy.shape(); let w = final_wh.shape();\nif c != w || c.last() != Some(&2) {\n    return Err(anyhow!(\"cannot concat cxcy {c:?} with wh {w:?}\"));\n}","typeGuard":"fn concat_halves_ok(cxcy: &mlx_rs::Array, wh: &mlx_rs::Array) -> bool {\n    cxcy.shape() == wh.shape() && cxcy.shape().last() == Some(&2)\n}","tryCatchPattern":"match decoder.forward(&enc, &refpoints) {\n    Err(Error::Inference(m)) if m.contains(\"boxes cat\") => {\n        eprintln!(\"box halves not concatenable: {m}\");\n        // fall back to a previous known-good checkpoint or skip the frame\n        fallback_decode(&enc)\n    }\n    other => other,\n}","preventionTips":["Assert both halves are (B, 300, 2) upstream; fix the producer, not the concat.","Squeeze stray axes right after each split/mul instead of at the end.","Add a golden-output test asserting final boxes are (B, 300, 4)."],"tags":["mlx","shape-mismatch","concatenate","bbox","rust"],"backgroundTag":"mlx-array-concat-shape-mismatch","analyzedSha":"4ebf712990fee17eeaf904dacf749b6e96ac9bf3","analyzedAt":"2026-09-01T23:33:43.065Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}