{"record":{"id":"f71148ce9e6de260","repo":"ruvnet/RuView","slug":"invalid-enrollment-e","errorCode":null,"errorMessage":"invalid enrollment: {e}","messagePattern":"invalid enrollment: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v2/crates/wifi-densepose-cli/src/room.rs","lineNumber":245,"sourceCode":"    #[arg(long, default_value = \"./room-bank.json\")]\n    pub output: String,\n    /// Optional transceiver-geometry file: a JSON array of `NodeGeometry`\n    /// records (ADR-152 §2.1.1). Recorded into the enrollment session before\n    /// training so the bank carries the layout it was trained under.\n    #[arg(long)]\n    pub geometry: Option<String>,\n}\n\n/// 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());","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/v2/crates/wifi-densepose-cli/src/room.rs#L227-L263","documentation":"The enrollment file was read but serde_json::from_str could not deserialize it into EnrollmentData. Causes: malformed JSON, or valid JSON whose shape does not match the schema — missing or renamed required fields (room_id, baseline_id, fs_hz, anchors, session), wrong types, or an enrollment written by an older schema.","triggerScenarios":"Hand-edited enrollment JSON with a typo or missing field; passing another JSON artifact (room-bank.json, a geometry file) as --enrollment; schema drift between the build that enrolled and the one training.","commonSituations":"Editing the file to rename a room or tweak fs_hz and breaking a field name; upgrading the CLI between enrollment and training; hand-authored files from other tooling.","solutions":["Validate the JSON and compare field names against EnrollmentData (room_id, baseline_id, fs_hz, anchors, session)","Re-run enroll with the current build if the file came from an older schema","Use --geometry for the geometry snapshot; --enrollment must be the enroll output","Fix the syntax error at the exact line/column the {e} payload reports"],"exampleFix":"// before\n{ \"room\": \"living\", \"baseline_id\": \"…\", \"fs_hz\": 100.0, \"anchors\": [], \"session\": {} }\n\n// after\n{ \"room_id\": \"living\", \"baseline_id\": \"…\", \"fs_hz\": 100.0, \"anchors\": [], \"session\": {} }","handlingStrategy":"validation","validationCode":"fn enrollment_shape_ok(raw: &str) -> bool {\n    let v: serde_json::Value = match serde_json::from_str(raw) {\n        Ok(v) => v,\n        Err(_) => return false,\n    };\n    for k in [\"room_id\", \"baseline_id\", \"fs_hz\", \"anchors\", \"session\"] {\n        if v.get(k).is_none() {\n            return false;\n        }\n    }\n    v.get(\"anchors\").map(|a| a.is_array()).unwrap_or(false)\n}","typeGuard":"fn as_enrollment(raw: &str) -> Option<EnrollmentData> {\n    serde_json::from_str(raw).ok()\n}","tryCatchPattern":null,"preventionTips":["Never hand-edit enrollment artifacts; re-enroll instead","Keep enrolling and training builds on the same version","If you fork the workflow, add a schema version field and check it before training"],"tags":["rust","serde","json","enrollment","schema"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}