Automattic/harper · critical

Failed to load curated dictionary: {}

Error message

Failed to load curated dictionary: {}

What it means

MutableDictionary's lazy static initializer panics if the curated dictionary bundled into the binary (dictionary.dict + annotations.json, via include_str!) fails to parse. Since the files are embedded at compile time, this panic indicates a corrupted/incompatible build artifact or a bug in the parser rather than a runtime environment problem.

Source

Thrown at harper-core/src/spell/mutable_dictionary.rs:38

/// as it is much faster.
///
/// To combine the contents of multiple dictionaries, regardless of type, use
/// [`super::MergedDictionary`].
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct MutableDictionary {
    /// All English words
    word_map: WordMap,
}

/// The uncached function that is used to produce the original copy of the
/// curated dictionary.
fn uncached_inner_new() -> Arc<MutableDictionary> {
    MutableDictionary::from_rune_files(
        include_str!("../../dictionary.dict"),
        include_str!("../../annotations.json"),
    )
    .map(Arc::new)
    .unwrap_or_else(|e| panic!("Failed to load curated dictionary: {}", e))
}

static DICT: LazyLock<Arc<MutableDictionary>> = LazyLock::new(uncached_inner_new);

impl MutableDictionary {
    pub fn new() -> Self {
        Self {
            word_map: WordMap::default(),
        }
    }

    pub fn from_rune_files(word_list: &str, attr_list: &str) -> Result<Self, rune::Error> {
        let word_list = parse_word_list(word_list)?;
        let attr_list = AttributeList::parse(attr_list)?;

        // There will be at _least_ this number of words
        let mut word_map = WordMap::default();

View on GitHub (pinned to 5fe7d5ab76)

Solutions

  1. Run `cargo clean` (or remove target/) and rebuild to rule out stale embedded artifacts.
  2. Verify dictionary.dict and annotations.json in harper-core are intact and match the current parser format.
  3. Update harper-core to the latest release; if it persists, file a bug with the wrapped error message.
  4. In application code, avoid relying on curated defaults and construct a dictionary explicitly to get a Result you can handle.

Example fix

// before
let dict = MutableDictionary::curated(); // panics on failure
// after
let dict = MutableDictionary::from_rune_files(DICT_PATH, ANNOTATIONS_PATH)
    .unwrap_or_else(|e| eprintln!("dict load failed: {e}"); MutableDictionary::new());
Defensive patterns

Strategy: fallback

Validate before calling

// Compile-time/build check: ensure embedded assets exist and are non-empty
// test: assert!(include_str!("../../dictionary.dict").len() > 0);

Try / catch

// Panic cannot be caught in-process; prefer fallible construction:
let dict = MutableDictionary::from_rune_files(dict_path, annotations_path)
    .unwrap_or_else(|e| { log::error("curated dict failed: {e}"); MutableDictionary::new() });

Prevention

When it happens

Trigger: First use of the curated dictionary (DICT LazyLock) triggers uncached_inner_new; it panics if MutableDictionary::from_rune_files returns Err — e.g. the embedded dictionary file is corrupt, an incompatible format version, or a parsing bug after a dictionary format change.

Common situations: Building with a mismatched harper-core version or a stale build cache after a dictionary format change; custom forks that replaced dictionary.dict with malformed content; upstream regression in dictionary data.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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