Automattic/harper · warning
Settings must contain a "harper-ls" key.
Error message
Settings must contain a "harper-ls" key.
What it means
Config::from_lsp_config found the top-level settings object but it lacks an object-valued "harper-ls" key. All Harper language-server options live under that namespace, so it is mandatory in the settings payload.
Source
Thrown at harper-ls/src/config.rs:96
/// Maximum length (in bytes) a file can have before it's skipped.
/// Above this limit, the file will not be linted.
pub max_file_length: usize,
pub exclude_patterns: GlobSet,
}
impl Config {
pub fn from_lsp_config(workspace_root: &Path, value: Value) -> Result<Self> {
let mut base = Config::default();
let workspace_root = workspace_root.canonicalize()?;
let workspace_root = workspace_root.as_path();
let Value::Object(value) = value else {
bail!("Settings must be an object.");
};
let Some(Value::Object(value)) = value.get("harper-ls") else {
bail!("Settings must contain a \"harper-ls\" key.");
};
if let Some(v) = value.get("userDictPath") {
if !v.is_string() {
bail!("userDict path must be a string.");
}
let path = v.as_str().unwrap();
if !path.is_empty() {
base.user_dict_path = path.try_resolve_in(workspace_root)?.to_path_buf();
}
}
if let Some(v) = value.get("fileDictPath") {
if !v.is_string() {
bail!("fileDict path must be a string.");
}
View on GitHub (pinned to 5fe7d5ab76)
Solutions
- Nest all options under the "harper-ls" key: settings = { "harper-ls": { ... } }.
- Verify your editor config uses the exact server name "harper-ls" (case-sensitive).
- Send an empty object ({"harper-ls": {}}) if you only want defaults.
Example fix
// before
{ "userDictPath": "./dict.txt" }
// after
{ "harper-ls": { "userDictPath": "./dict.txt" } } Defensive patterns
Strategy: validation
Validate before calling
if (!settings || typeof settings['harper-ls'] !== 'object' || settings['harper-ls'] === null) {
throw new Error('settings must contain a "harper-ls" object');
} Type guard
fn has_harper_ls_section(v: &serde_json::Value) -> bool {
v.get("harper-ls").map_or(false, |s| s.is_object())
} Try / catch
match Config::from_lsp_config(value, workspace_root) {
Ok(cfg) => cfg,
Err(e) => { log::warn!("invalid settings: {e}"); Config::default() }
} Prevention
- Nest all harper-ls options under the exact key "harper-ls".
- Check your editor's LSP server name matches "harper-ls" exactly.
- Start from documented example settings blocks instead of writing them from scratch.
When it happens
Trigger: Sending settings like {"otherServer": {...}} or {"harper-ls": "enabled"} (non-object) to harper-ls via initialize options or didChangeConfiguration.
Common situations: Configuring harper-ls under the wrong server name in the editor's LSP settings; nesting harper-ls options at the top level instead of under "harper-ls"; sending harper-ls settings as a string or list.
Related errors
- The code action configuration must be an object.
- ForceStable must be a boolean value.
- Settings must be an object.
- userDict path must be a string.
- fileDict path must be a string.
AI-assisted analysis of Automattic/harper@5fe7d5ab76 (2026-09-06).
Data as JSON: /api/errors/40b0b86607a769b0.
Report an issue: GitHub.