{"record":{"id":"1d35fd9b0116a97c","repo":"Automattic/harper","slug":"userdict-path-must-be-a-string","errorCode":null,"errorMessage":"userDict path must be a string.","messagePattern":"userDict path must be a string\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"harper-ls/src/config.rs","lineNumber":101,"sourceCode":"\nimpl Config {\n    pub fn from_lsp_config(workspace_root: &Path, value: Value) -> Result<Self> {\n        let mut base = Config::default();\n\n        let workspace_root = workspace_root.canonicalize()?;\n        let workspace_root = workspace_root.as_path();\n\n        let Value::Object(value) = value else {\n            bail!(\"Settings must be an object.\");\n        };\n\n        let Some(Value::Object(value)) = value.get(\"harper-ls\") else {\n            bail!(\"Settings must contain a \\\"harper-ls\\\" key.\");\n        };\n\n        if let Some(v) = value.get(\"userDictPath\") {\n            if !v.is_string() {\n                bail!(\"userDict path must be a string.\");\n            }\n\n            let path = v.as_str().unwrap();\n            if !path.is_empty() {\n                base.user_dict_path = path.try_resolve_in(workspace_root)?.to_path_buf();\n            }\n        }\n\n        if let Some(v) = value.get(\"fileDictPath\") {\n            if !v.is_string() {\n                bail!(\"fileDict path must be a string.\");\n            }\n\n            let path = v.as_str().unwrap();\n            if !path.is_empty() {\n                base.file_dict_path = path.try_resolve_in(workspace_root)?.to_path_buf();\n            }\n        }","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/Automattic/harper/blob/5fe7d5ab76492d83f3ecdbc3f1da83c75dcb6f83/harper-ls/src/config.rs#L83-L119","documentation":"Inside Config::from_lsp_config, the optional `userDictPath` setting must be a JSON string if present. Non-string values abort config parsing. When the string is non-empty it is resolved relative to the workspace root; empty strings are ignored (default dictionary used).","triggerScenarios":"Sending {\"harper-ls\": {\"userDictPath\": 42}} or an array/object for userDictPath via LSP settings.","commonSituations":"Editor settings where the path was written as a list or with wrong type; tooling that programmatically builds settings and passes a Path object that serializes as a non-string; typos in config generators.","solutions":["Set userDictPath to a string path, e.g. \"userDictPath\": \"./user-dict.txt\".","Remove the key to use the default dictionary location.","If generating settings programmatically, ensure paths are converted to strings (and use empty string \"\" explicitly to mean 'no custom dict')."],"exampleFix":"// before\n{ \"harper-ls\": { \"userDictPath\": [\"dict.txt\"] } }\n// after\n{ \"harper-ls\": { \"userDictPath\": \"dict.txt\" } }","handlingStrategy":"validation","validationCode":"const p = settings?.['harper-ls']?.userDictPath;\nif (p !== undefined && typeof p !== 'string') {\n  throw new TypeError('userDictPath must be a string');\n}","typeGuard":"fn is_optional_string(v: &serde_json::Value) -> bool {\n    v.get(\"userDictPath\").map_or(true, |s| s.is_string())\n}","tryCatchPattern":"match Config::from_lsp_config(value, workspace_root) {\n    Ok(cfg) => cfg,\n    Err(e) => { log::warn!(\"invalid userDictPath: {e}\"); Config::default() }\n}","preventionTips":["Always write userDictPath as a quoted string path in settings.","Convert Path objects to strings before embedding them in generated settings.","Remember empty string means 'use default' — prefer omitting the key unless customizing."],"tags":["rust","lsp","configuration","json"],"backgroundTag":"invalid-config-value","analyzedSha":"5fe7d5ab76492d83f3ecdbc3f1da83c75dcb6f83","analyzedAt":"2026-09-06T11:34:38.610Z","contentChangedAt":"2026-09-06T11:34:38.610Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}