{"record":{"id":"ecf474a7c47a2322","repo":"Automattic/harper","slug":"settings-must-be-an-object","errorCode":null,"errorMessage":"Settings must be an object.","messagePattern":"Settings must be an object\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"harper-ls/src/config.rs","lineNumber":92,"sourceCode":"    pub code_action_config: CodeActionConfig,\n    pub isolate_english: bool,\n    pub markdown_options: MarkdownOptions,\n    pub dialect: Dialect,\n    /// Maximum length (in bytes) a file can have before it's skipped.\n    /// Above this limit, the file will not be linted.\n    pub max_file_length: usize,\n    pub exclude_patterns: GlobSet,\n}\n\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\") {","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/Automattic/harper/blob/5fe7d5ab76492d83f3ecdbc3f1da83c75dcb6f83/harper-ls/src/config.rs#L74-L110","documentation":"Config::from_lsp_config expects the top-level LSP settings value to be a JSON object. Non-object values (null, array, scalar) are rejected because the server needs to look up the \"harper-ls\" key inside it.","triggerScenarios":"A didChangeConfiguration notification or initialize options where `settings` is null or not an object — typical when the client sends an empty/null settings payload on startup.","commonSituations":"Editor clients that fire didChangeConfiguration with null settings before real config arrives; misconfigured LSP client setup passing the wrong value as settings; testing the server manually with non-object JSON.","solutions":["Ensure the client sends settings as an object; on the server, short-circuit to Config::default() when settings is null.","Fix your LSP client configuration so workspace settings are an object containing a \"harper-ls\" section.","If triggering manually (e.g. via an LSP REPL), send {\"harper-ls\": {}} rather than null."],"exampleFix":"// before\nclient.notify(\"workspace/didChangeConfiguration\", { settings: null });\n// after\nclient.notify(\"workspace/didChangeConfiguration\", { settings: { \"harper-ls\": {} } });","handlingStrategy":"validation","validationCode":"if (params.settings === null || typeof params.settings !== 'object') {\n  params.settings = { 'harper-ls': {} };\n}","typeGuard":"fn is_settings_object(v: &serde_json::Value) -> bool {\n    matches!(v, serde_json::Value::Object(_))\n}","tryCatchPattern":"match Config::from_lsp_config(value, workspace_root) {\n    Ok(cfg) => cfg,\n    Err(_) => Config::default(),\n}","preventionTips":["Send an object (even empty) for settings rather than null from the client.","On the server, short-circuit to defaults when settings is null.","Test your LSP client's didChangeConfiguration payloads during setup."],"tags":["rust","lsp","configuration","json"],"backgroundTag":"config-type-mismatch","analyzedSha":"5fe7d5ab76492d83f3ecdbc3f1da83c75dcb6f83","analyzedAt":"2026-09-06T11:34:38.610Z","contentChangedAt":"2026-09-06T11:34:38.610Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}