{"record":{"id":"40d1e8c58549b1ec","repo":"ruvnet/RuView","slug":"invalid-geometry-path-e-expected-a-json-arra","errorCode":null,"errorMessage":"invalid geometry {path}: {e} (expected a JSON array of NodeGeometry records)","messagePattern":"invalid geometry (.+?): (.+?) \\(expected a JSON array of NodeGeometry records\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v2/crates/wifi-densepose-cli/src/room.rs","lineNumber":254,"sourceCode":"/// Execute `train-room`.\n///\n/// If the enrollment session carries a transceiver-geometry snapshot (recorded\n/// at enroll time or supplied here via `--geometry`), it is threaded into the\n/// bank (ADR-152 §2.1.1); a geometry-free enrollment still trains a valid bank.\npub async fn train_room(args: TrainRoomArgs) -> Result<()> {\n    let raw = std::fs::read_to_string(&args.enrollment)\n        .map_err(|e| anyhow::anyhow!(\"cannot read {}: {e} — run `enroll` first\", args.enrollment))?;\n    let mut data: EnrollmentData =\n        serde_json::from_str(&raw).map_err(|e| anyhow::anyhow!(\"invalid enrollment: {e}\"))?;\n    if data.anchors.is_empty() {\n        bail!(\"no accepted anchors in {} — re-run enroll\", args.enrollment);\n    }\n\n    if let Some(path) = &args.geometry {\n        let graw = std::fs::read_to_string(path)\n            .map_err(|e| anyhow::anyhow!(\"cannot read geometry {path}: {e}\"))?;\n        let geometry: Vec<NodeGeometry> = serde_json::from_str(&graw).map_err(|e| {\n            anyhow::anyhow!(\"invalid geometry {path}: {e} (expected a JSON array of NodeGeometry records)\")\n        })?;\n        data.session.record_geometry(geometry, now_unix());\n    }\n\n    let mut bank = SpecialistBank::train(&data.room_id, &data.baseline_id, &data.anchors, now_unix())\n        .map_err(|e| anyhow::anyhow!(\"training failed: {e}\"))?;\n    match data.session.geometry() {\n        Some(g) if !g.is_empty() => {\n            bank = bank.with_geometry(g.to_vec());\n            eprintln!(\n                \"[train-room] geometry: {} node(s) snapshotted into the bank (ADR-152 §2.1.1)\",\n                bank.geometry.len()\n            );\n        }\n        _ => eprintln!(\n            \"[train-room] no transceiver geometry recorded — bank will not support geometry conditioning (ADR-152 §2.1.2)\"\n        ),\n    }","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/v2/crates/wifi-densepose-cli/src/room.rs#L236-L272","documentation":"The geometry file was read but its JSON did not deserialize into Vec<NodeGeometry> — the message spells out the expected shape ('a JSON array of NodeGeometry records'). Causes: a JSON object instead of a top-level array, missing or renamed NodeGeometry fields, wrong types, or malformed JSON.","triggerScenarios":"Passing a single object instead of an array; exporting geometry from survey tooling whose field names differ from NodeGeometry's serde schema; truncated files; passing the enrollment JSON as --geometry.","commonSituations":"Hand-authored geometry from site surveys; field-name drift (node identifiers, position keys) between producer tools and the NodeGeometry struct.","solutions":["Wrap records in a top-level array and match NodeGeometry field names exactly as the struct's serde attributes define them","Verify with a quick deserialization probe (serde_json::from_str::<Vec<NodeGeometry>>) before invoking the CLI","Write a converter for genuinely different survey formats instead of ad-hoc edits","Omit --geometry to proceed without geometry conditioning"],"exampleFix":"// before\n{ \"node_id\": \"n1\", \"position_m\": [1.0, 2.0] }\n\n// after\n[ { \"node_id\": \"n1\", \"position_m\": [1.0, 2.0] }, { \"node_id\": \"n2\", \"position_m\": [3.5, 0.5] } ]","handlingStrategy":"validation","validationCode":"fn geometry_shape_ok(raw: &str) -> bool {\n    serde_json::from_str::<Vec<NodeGeometry>>(raw).is_ok()\n}","typeGuard":"fn as_geometry(raw: &str) -> Option<Vec<NodeGeometry>> {\n    serde_json::from_str(raw).ok()\n}","tryCatchPattern":null,"preventionTips":["Generate geometry files with the same serde structs that consume them","Validate geometry JSON in CI for every surveyed room snapshot","Remember the top level must be an array, not a single object"],"tags":["rust","serde","json","geometry","schema"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}