{"record":{"id":"1e51532b769e0af2","repo":"BoundaryML/baml","slug":"key-must-be-a-string","errorCode":null,"errorMessage":"Key must be a string","messagePattern":"Key must be a string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/language_client_cffi/src/ctypes/cffi_value_decode.rs","lineNumber":54,"sourceCode":"                    .map(from_host_map_entry)\n                    .collect::<Result<_, _>>()?;\n                Value::Class(c.name, fields, ())\n            }\n            HostVal::EnumValue(e) => Value::Enum(e.name, e.value, ()),\n            HostVal::Handle(handle) => {\n                let raw_ptr = RawPtrType::decode(handle)?;\n                Value::RawPtr(raw_ptr, ())\n            }\n        })\n    }\n}\n\npub(super) fn from_host_map_entry(\n    item: crate::baml::cffi::HostMapEntry,\n) -> Result<(String, Value), anyhow::Error> {\n    let key = match item.key {\n        Some(crate::baml::cffi::host_map_entry::Key::StringKey(k)) => k,\n        _ => return Err(anyhow::anyhow!(\"Key must be a string\")),\n    };\n    let value = item\n        .value\n        .ok_or(anyhow::anyhow!(\"Value is null for key {}\", key))?;\n    Ok((key, Value::decode(value)?))\n}\n","sourceCodeStart":36,"sourceCodeEnd":61,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/language_client_cffi/src/ctypes/cffi_value_decode.rs#L36-L61","documentation":"Thrown by from_host_map_entry when a HostMapEntry's key oneof is not StringKey (it is IntKey, BoolKey, EnumKey, or unset). The plain CFFI Value decoder only supports string keys, so any other key type is rejected with this fixed message.","triggerScenarios":"Passing a map/dict with non-string keys (int or bool keys) from the host language into a BAML function call that decodes to a Value map.","commonSituations":"Python/JS developers passing {1: \"x\"} or {True: \"y\"} as kwargs or map parameters; language defaults that produce integer keys from JSON-like literals.","solutions":["Convert all map keys to strings on the host side before passing them to the BAML client (e.g. str(key) in Python)","If non-string keys are semantically needed, encode them as strings and decode host-side after the call","Check for JSON round-trips that convert string keys to numeric ones and normalize keys after parsing"],"exampleFix":"// before (Python)\nkwargs = {1: \"one\"}\n\n// after\nkwargs = {str(k): v for k, v in kwargs.items()}","handlingStrategy":"validation","validationCode":"def stringify_keys(d):\n    if not all(isinstance(k, str) for k in d):\n        raise TypeError(\"All map keys must be strings\")\n    return {str(k): v for k, v in d.items()}\n\nkwargs = stringify_keys(raw_kwargs)","typeGuard":"def has_string_keys(d: dict) -> bool:\n    return all(isinstance(k, str) for k in d.keys())","tryCatchPattern":"try:\n    result = client.CallFunction(fn, kwargs)\nexcept Exception as e:\n    if \"Key must be a string\" in str(e):\n        kwargs = {str(k): v for k, v in kwargs.items()}\n        result = client.CallFunction(fn, kwargs)\n    else:\n        raise","preventionTips":["Normalize dict keys to strings at the data-source boundary (JSON, DB rows)","Watch for languages/frameworks that allow int or bool keys in maps","Add a pre-call schema check for kwargs key types"],"tags":["ffi","decode","map-keys","rust"],"backgroundTag":"type-mismatch","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"}