dotnet/wpf · error · ArgumentException

SR.Format(SR.CustomDictionaryFailedToLoadDictionaryUri…

Error message

SR.Format(SR.CustomDictionaryFailedToLoadDictionaryUri, lexiconFilePath)

What it means

WinRTSpellerInterop.LoadDictionaryImpl throws ArgumentException when the custom dictionary (lexicon) file does not exist at the supplied path. WPF spell-checking custom dictionaries must be a real file on disk before being registered, so the API fails fast with a message naming the missing path.

Solutions

  1. Verify the lexicon file exists before adding it: check File.Exists(lexiconFilePath) and create or fix the path if false.
  2. Ensure the dictionary file is included in the project as Content with CopyToOutputDirectory set, so it ships with the app.
  3. Use an absolute path (Path.Combine(AppContext.BaseDirectory, "dictionary.lex")) instead of a relative one.
  4. Catch ArgumentException around the dictionary registration and fall back to default spell-checking.

Example fix

// before
speller.CustomDictionaries.Add(new Uri(relativeLexPath));
// after
string lexPath = Path.Combine(AppContext.BaseDirectory, "custom.lex");
if (!File.Exists(lexPath))
    throw new FileNotFoundException("Custom dictionary missing", lexPath);
speller.CustomDictionaries.Add(new Uri(lexPath));
Defensive patterns

Strategy: validation

Validate before calling

if (string.IsNullOrEmpty(lexiconFilePath) || !File.Exists(lexiconFilePath))
    throw new FileNotFoundException($"Lexicon not found: {lexiconFilePath}");

Try / catch

try { speller.CustomDictionaries.Add(dictUri); }
catch (ArgumentException ex) { Log.Warn($"Dictionary skipped: {ex.Message}"); }

Prevention

When it happens

Trigger: Calling SpellingReform/CustomDictionaries.Add (via LoadDictionary -> LoadDictionaryImpl) with a lexiconFilePath for which System.IO.File.Exists returns false — e.g. a typo'd path, a file deleted after registration, or a path in a location the process cannot see.

Common situations: Deploying an app that ships a .lex file but the file wasn't copied to the output directory; using a relative path whose current working directory differs; referencing a dictionary in a user profile that doesn't exist under another account; cleaning/publishing removing content files.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/92326115d59ad0cf. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WinRTSpellerInterop.cs:430

        ///
        ///     If no culture is specified in the first line of <paramref name="lexiconFilePath"/>
        ///     in the format #LID nnnn (where nnnn = decimal LCID of the culture), then invariant
        ///     culture is returned.
        /// </returns>
        /// <remarks>
        ///     At the end of this method, we guarantee that <paramref name="lexiconFilePath"/>
        ///     can be reclaimed (i.e., potentially deleted) by the caller.
        /// </remarks>
        private Tuple<string, string> LoadDictionaryImpl(string lexiconFilePath)
        {
            if (_isDisposed)
            {
                return new Tuple<string, string>(null, null);
            }

            if (!File.Exists(lexiconFilePath))
            {
                throw new ArgumentException(SR.Format(SR.CustomDictionaryFailedToLoadDictionaryUri, lexiconFilePath));
            }

            bool fileCopied = false;
            string lexiconPrivateCopyPath = null;

            try
            {
                CultureInfo culture = null;

                // Read the first line of the file and detect culture, if specified
                using (FileStream stream = new FileStream(lexiconFilePath, FileMode.Open, FileAccess.Read))
                {
                    string line = null;
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        line = reader.ReadLine();
                        culture = WinRTSpellerInterop.TryParseLexiconCulture(line);
                    }

View on GitHub (pinned to 81131a70a4)