Automattic/harper · warning

Settings must be an object.

Error message

Settings must be an object.

What it means

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.

Source

Thrown at harper-ls/src/config.rs:92

    pub code_action_config: CodeActionConfig,
    pub isolate_english: bool,
    pub markdown_options: MarkdownOptions,
    pub dialect: Dialect,
    /// 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") {

View on GitHub (pinned to 5fe7d5ab76)

Solutions

  1. Ensure the client sends settings as an object; on the server, short-circuit to Config::default() when settings is null.
  2. Fix your LSP client configuration so workspace settings are an object containing a "harper-ls" section.
  3. If triggering manually (e.g. via an LSP REPL), send {"harper-ls": {}} rather than null.

Example fix

// before
client.notify("workspace/didChangeConfiguration", { settings: null });
// after
client.notify("workspace/didChangeConfiguration", { settings: { "harper-ls": {} } });
Defensive patterns

Strategy: validation

Validate before calling

if (params.settings === null || typeof params.settings !== 'object') {
  params.settings = { 'harper-ls': {} };
}

Type guard

fn is_settings_object(v: &serde_json::Value) -> bool {
    matches!(v, serde_json::Value::Object(_))
}

Try / catch

match Config::from_lsp_config(value, workspace_root) {
    Ok(cfg) => cfg,
    Err(_) => Config::default(),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Automattic/harper@5fe7d5ab76 (2026-09-06). Data as JSON: /api/errors/ecf474a7c47a2322. Report an issue: GitHub.