Automattic/harper · error · anyhow::Error

Unable to save the dictionary to file: {err}

Error message

Unable to save the dictionary to file: {err}

What it means

`save_user_dictionary` writes the user's mutable dictionary to the configured `user_dict_path` via `save_dict`. When the underlying file write fails (I/O error, missing directory, permissions), the error is wrapped with "Unable to save the dictionary to file" and propagated from the `execute_command` handler (e.g. add-word-to-dictionary).

Source

Thrown at harper-ls/src/backend.rs:162

        .await
        .context("Unable to save the dictionary to path.")
    }

    async fn load_user_dictionary(&self) -> MutableDictionary {
        let config = self.config.read().await;

        load_dict(&config.user_dict_path, self.config.read().await.dialect)
            .await
            .map_err(|err| info!("{err}"))
            .unwrap_or(MutableDictionary::new())
    }

    async fn save_user_dictionary(&self, dict: impl Dictionary) -> Result<()> {
        let config = self.config.read().await;

        save_dict(&config.user_dict_path, dict)
            .await
            .map_err(|err| anyhow!("Unable to save the dictionary to file: {err}"))
    }

    async fn load_workspace_dictionary(&self) -> MutableDictionary {
        let config = self.config.read().await;
        load_dict(
            &config.workspace_dict_path,
            self.config.read().await.dialect,
        )
        .await
        .map_err(|err| info!("{err}"))
        .unwrap_or(MutableDictionary::new())
    }

    async fn save_workspace_dictionary(&self, dict: impl Dictionary) -> Result<()> {
        let config = self.config.read().await;
        save_dict(&config.workspace_dict_path, dict)
            .await
            .map_err(|err| anyhow!("Unable to save the dictionary to file: {err}"))

View on GitHub (pinned to 5fe7d5ab76)

Solutions

  1. Create the parent directory of the configured user dict path and ensure it is writable.
  2. Check `user_dict_path` in your config points to a valid, writable file location.
  3. Fix filesystem permissions (or run outside a read-only mount/container).
  4. Verify disk space and that the path is not itself a directory.

Example fix

// before (config)
{ "userDictPath": "./missing-dir/user-dict.txt" }
// after
// mkdir missing-dir first, or point at an existing writable dir:
{ "userDictPath": "./user-dict.txt" }
Defensive patterns

Strategy: try-catch

Validate before calling

import fs from 'node:fs';
const dir = path.dirname(userDictPath);
fs.mkdirSync(dir, { recursive: true });
fs.accessSync(dir, fs.constants.W_OK);

Try / catch

try {
  await client.sendRequest('workspace/executeCommand', { command: 'harperLs.addUserWord', arguments: [word] });
} catch (err) {
  if (String(err.message).includes('Unable to save the dictionary to file')) {
    // fix user_dict_path permissions/existence, then retry
  }
}

Prevention

When it happens

Trigger: Calling the add-to-dictionary LSP command (or execute_command path) when `user_dict_path` points to a nonexistent directory, a read-only location, or a path the process cannot create/write.

Common situations: user_dict_path configured relative to a workspace root that doesn't exist; read-only home or container filesystem; parent directory deleted while the server runs; path points to a directory rather than a file.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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