{"record":{"id":"e6ff479c99738f45","repo":"Zackriya-Solutions/meetily","slug":"metadata-value-checked-as-object","errorCode":null,"errorMessage":"metadata value checked as object","messagePattern":"metadata value checked as object","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"frontend/src-tauri/src/summary/metadata.rs","lineNumber":80,"sourceCode":"    summary_language: Option<&str>,\n) -> Result<()> {\n    let _guard = METADATA_WRITE_LOCK.lock().unwrap_or_else(|poisoned| poisoned.into_inner());\n    let metadata_path = metadata_path(folder);\n    let temp_path = metadata_temp_path(folder);\n\n    let mut value = if metadata_path.exists() {\n        let raw = std::fs::read_to_string(&metadata_path)\n            .with_context(|| format!(\"Failed to read {}\", metadata_path.display()))?;\n        parse_metadata_json(&raw)?\n    } else {\n        Value::Object(serde_json::Map::new())\n    };\n\n    if !value.is_object() {\n        bail!(\"Failed to parse metadata.json: root value must be a JSON object\");\n    }\n\n    let object = value.as_object_mut().expect(\"metadata value checked as object\");\n    match summary_language {\n        Some(code) => {\n            let normalised = normalise_supported_summary_language(code)?;\n            object.insert(field.to_string(), Value::String(normalised));\n        }\n        None => {\n            object.remove(field);\n        }\n    }\n\n    let json_string = serde_json::to_string_pretty(&value)\n        .context(\"Failed to serialize metadata.json\")?;\n    std::fs::write(&temp_path, json_string)\n        .with_context(|| format!(\"Failed to write {}\", temp_path.display()))?;\n    std::fs::rename(&temp_path, &metadata_path).with_context(|| {\n        format!(\n            \"Failed to replace {} with {}\",\n            metadata_path.display(),","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/Zackriya-Solutions/meetily/blob/0281737d87d26352fb0adc78c8c0975f691b23d1/frontend/src-tauri/src/summary/metadata.rs#L62-L98","documentation":"serde_json's Value::as_object_mut() returns Some only when the root value is a JSON object. The code checks value.is_object() and bails with a proper error otherwise, so the .expect(\"metadata value checked as object\") is a correctly defended invariant — reachable only if future edits mutate value's variant between the check and the use.","triggerScenarios":"Only via a refactor that reassigns `value` (or calls code that can) between the is_object() check and the expect; as written the two lines are adjacent and the panic is unreachable.","commonSituations":"Never observed; this check-then-expect pairing is the recommended pattern for narrowing serde_json roots without cloning.","solutions":["None required — the guard is already correct","Optionally make the narrowing explicit with let-else: `let Value::Object(object) = &mut value else { bail!(...) };`","Keep the is_object check directly adjacent to the as_object_mut call in future edits"],"exampleFix":"// after (idiomatic narrowing, no expect)\nlet Value::Object(object) = &mut value else {\n    bail!(\"Failed to parse metadata.json: root value must be a JSON object\");\n};","handlingStrategy":"type-guard","validationCode":"if !value.is_object() {\n    anyhow::bail!(\"metadata.json root must be a JSON object\");\n}","typeGuard":"fn is_json_object(v: &serde_json::Value) -> bool {\n    v.is_object()\n}","tryCatchPattern":null,"preventionTips":["Keep the is_object check immediately before as_object_mut; never split them across calls","Prefer let-else narrowing (`let Value::Object(o) = ... else`) to make invalid states unrepresentable","Validate external JSON at the boundary (parse_metadata_json) so downstream code can assume the shape"],"tags":["rust","serde-json","invariant","metadata","type-narrowing"],"backgroundTag":"option-expect-invariant-panic","analyzedSha":"0281737d87d26352fb0adc78c8c0975f691b23d1","analyzedAt":"2026-08-16T20:57:52.567Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}