{"record":{"id":"1f1dffccb409581d","repo":"BoundaryML/baml","slug":"failed-to-deserialize-value-e","errorCode":null,"errorMessage":"Failed to deserialize value: {e}","messagePattern":"Failed to deserialize value: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/baml-lib/baml-types/src/value_expr.rs","lineNumber":585,"sourceCode":"        match self.resolve(ctx) {\n            Ok(ResolvedValue::Numeric(n, ..)) => Ok(n),\n            _ => Err(anyhow::anyhow!(\"Expected a numeric value\")),\n        }\n    }\n\n    pub fn resolve_null(&self, ctx: &impl GetEnvVar) -> Result<()> {\n        match self.resolve(ctx) {\n            Ok(ResolvedValue::Null(..)) => Ok(()),\n            _ => Err(anyhow::anyhow!(\"Expected a null value\")),\n        }\n    }\n\n    pub fn resolve_serde<T: serde::de::DeserializeOwned>(&self, ctx: &impl GetEnvVar) -> Result<T> {\n        let value = self.resolve(ctx)?;\n        let value: serde_json::Value = value.try_into()?;\n        match serde_json::from_value(value) {\n            Ok(v) => Ok(v),\n            Err(e) => Err(anyhow::anyhow!(\"Failed to deserialize value: {e}\")),\n        }\n    }\n\n    /// Resolve and deserialize, with support for template_string calls.\n    pub fn resolve_serde_with_templates<T: serde::de::DeserializeOwned>(\n        &self,\n        ctx: &impl GetEnvVar,\n        template_renderer: &impl TemplateStringRenderer,\n    ) -> Result<T> {\n        let value = self.resolve_with_templates(ctx, template_renderer)?;\n        let value: serde_json::Value = value.try_into()?;\n        match serde_json::from_value(value) {\n            Ok(v) => Ok(v),\n            Err(e) => Err(anyhow::anyhow!(\"Failed to deserialize value: {e}\")),\n        }\n    }\n\n    /// Resolve the value to a [`ResolvedValue`], with support for template_string calls.","sourceCodeStart":567,"sourceCodeEnd":603,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/baml-lib/baml-types/src/value_expr.rs#L567-L603","documentation":"ValueExpr::resolve_serde resolves an expression to a serde_json::Value and then deserializes it into an arbitrary DeserializeOwned type T. If serde_json::from_value fails — the resolved JSON shape does not match T's structure — the error is wrapped as 'Failed to deserialize value: {e}' with the serde detail appended.","triggerScenarios":"Calling resolve_serde::<T>() where the resolved JSON does not structurally match T: wrong field types (string where number required), missing required fields, arrays where structs expected, or null for a non-Option field.","commonSituations":"Deserializing LLM-provider parameters or user config into a Rust struct after renaming a struct field, changing a type from String to u64, or making an Option field required.","solutions":["Read the serde detail after 'Failed to deserialize value:' — it names the exact field and expected type.","Update the target struct T to match the resolved JSON (add #[serde(default)], Option, or rename attributes).","Fix the config/expression so its shape matches T (correct types, no missing keys).","Use resolve_serde_with_templates if the expression contains template_string parts that need rendering first.","Inspect the intermediate serde_json::Value during development to see the actual shape."],"exampleFix":"// before\n#[derive(Deserialize)] struct Params { max_tokens: u32 } // value: {\"max_tokens\": \"512\"}\nlet p: Params = expr.resolve_serde(&ctx)?; // Failed to deserialize value: invalid type: string, expected u32\n// after\n// fix config: {\"max_tokens\": 512}  (or accept strings via a custom deserializer)\nlet p: Params = expr.resolve_serde(&ctx)?;","handlingStrategy":"try-catch","validationCode":"let value: serde_json::Value = expr.resolve(&ctx)?.try_into()?;\nserde_json::from_value::<T>(value.clone())\n    .map_err(|e| anyhow!(\"shape mismatch for {}: {e}\", std::any::type_name::<T>()))?;","typeGuard":"fn matches_shape<T: serde::de::DeserializeOwned>(v: &serde_json::Value) -> bool {\n    serde_json::from_value::<T>(v.clone()).is_ok()\n}","tryCatchPattern":"let parsed: T = expr.resolve_serde(&ctx)\n    .map_err(|e| anyhow::anyhow!(\"config deserialization failed; check field types: {e:#}\"))?;","preventionTips":["Keep target struct field types aligned with the config JSON (u32 vs string etc.).","Use #[serde(default)] / Option for optional fields.","Add serde rename attributes when config keys differ from Rust field names.","Inspect the resolved serde_json::Value during development before binding to T."],"tags":["rust","serde","deserialization","json"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}