{"record":{"id":"53b98372d8ac799e","repo":"Flow-Launcher/Flow.Launcher","slug":"e-message-53b983","errorCode":null,"errorMessage":"{e.Message}","messagePattern":"\\{e\\.Message\\}","errorType":"exception","errorClass":"SearchException","httpStatus":null,"severity":"error","filePath":"Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndex.cs","lineNumber":87,"sourceCode":"\n            // Initial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.\n        }\n\n        internal static IAsyncEnumerable<SearchResult> WindowsIndexSearchAsync(\n            string connectionString,\n            string search,\n            CancellationToken token)\n        {\n            try\n            {\n\n                return _reservedPatternMatcher.IsMatch(search)\n                    ? AsyncEnumerable.Empty<SearchResult>()\n                    : ExecuteWindowsIndexSearchAsync(search, connectionString, token);\n            }\n            catch (InvalidOperationException e)\n            {\n                throw new SearchException(\"Windows Index\", e.Message, e);\n            }\n        }\n\n        internal static bool PathIsIndexed(string path)\n        {\n            try\n            {\n                var csm = new CSearchManager();\n                var indexManager = csm.GetCatalog(\"SystemIndex\").GetCrawlScopeManager();\n                return indexManager.IncludedInCrawlScope(path) > 0;\n            }\n            catch (COMException)\n            {\n                // Occurs because the Windows Indexing (WSearch) is turned off in services and unable to be used by Explorer plugin\n                return false;\n            }\n        }\n    }","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/Flow-Launcher/Flow.Launcher/blob/7fc63b07bbaa10a0f0b2601487913fcba9ed24b0/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndex.cs#L69-L105","documentation":"Thrown as SearchException('Windows Index', e.Message, e) from WindowsIndex.WindowsIndexSearchAsync when the (synchronous) setup of the async enumerable throws InvalidOperationException. Because the body is an iterator, only exceptions raised before the first `yield` are caught here - notably the regex `_reservedPatternMatcher.IsMatch(search)` call. OLE DB failures happen later (inside ExecuteWindowsIndexSearchAsync) and are logged/yield-broken there, not here.","triggerScenarios":"The reserved-pattern regex throws InvalidOperationException - in practice this is rare, but it can occur if the regex engine hits an internal state error or if the search string is null at a callsite that bypassed the empty check. More commonly this catch defends against future changes to the synchronous prologue of the iterator.","commonSituations":"A caller passes a null `search` (the IsMatch call then throws ArgumentNullException, not InvalidOperationException, so this is mostly defensive); a regex engine regression on a specific Unicode input; refactoring that moves OLE DB connection setup above the first yield would route OleDbException through this catch.","solutions":["Guard the search argument at the top: if (string.IsNullOrEmpty(search)) return AsyncEnumerable.Empty<SearchResult>();","Move all OLE DB code below the first yield so failures stay inside ExecuteWindowsIndexSearchAsync and are logged, not rethrown as SearchException.","If SearchException is thrown, inspect InnerException for the real InvalidOperationException stack.","Add unit tests for the regex against adversarial inputs to make this branch unreachable."],"exampleFix":"// before\ninternal static IAsyncEnumerable<SearchResult> WindowsIndexSearchAsync(string connectionString, string search, CancellationToken token)\n{\n    try\n    {\n        return _reservedPatternMatcher.IsMatch(search)\n            ? AsyncEnumerable.Empty<SearchResult>()\n            : ExecuteWindowsIndexSearchAsync(search, connectionString, token);\n    }\n    catch (InvalidOperationException e)\n    {\n        throw new SearchException(\"Windows Index\", e.Message, e);\n    }\n}\n\n// after - guard input and let OLE DB failures stay in the iterator\ninternal static IAsyncEnumerable<SearchResult> WindowsIndexSearchAsync(string connectionString, string search, CancellationToken token)\n{\n    if (string.IsNullOrEmpty(search) || _reservedPatternMatcher.IsMatch(search))\n        return AsyncEnumerable.Empty<SearchResult>();\n    return ExecuteWindowsIndexSearchAsync(search, connectionString, token);\n}","handlingStrategy":"validation","validationCode":"if (string.IsNullOrEmpty(search))\n    return AsyncEnumerable.Empty<SearchResult>();","typeGuard":"static bool IsSearchable(string search) => !string.IsNullOrEmpty(search);","tryCatchPattern":"try { return WindowsIndex.WindowsIndexSearchAsync(connectionString, search, token); }\ncatch (SearchException ex) { App.API.LogException(nameof(WindowsIndex), $\"{ex.EngineName} query failed\", ex.InnerException); return AsyncEnumerable.Empty<SearchResult>(); }","preventionTips":["Guard against null/empty search at the top of WindowsIndexSearchAsync.","Keep all OLE DB code below the first yield so failures are logged, not rethrown.","Inspect SearchException.InnerException for the real InvalidOperationException.","Unit-test the reserved-pattern regex against adversarial inputs."],"tags":["windows-index","oledb","search-exception","async-enumerable","csharp"],"backgroundTag":null,"analyzedSha":"7fc63b07bbaa10a0f0b2601487913fcba9ed24b0","analyzedAt":"2026-08-13T14:54:20.914Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}