I have used AI tools for suggestions and guidance while preparing my contribution. I have reviewed and adapted the suggestions, and my contribution complies with the Generative AI Policy and the GNU General Public License (GPL) version 2 or later.
Taxonomy::removeOrphanNodes() reuses one delete query across passes and calls clear('where') before each whereIn(). clear('where') doesn't remove the values bound by the previous whereIn(), so on the second pass there are more bound values than placeholders and the PDO driver throws ExecutionFailureException: HY093. A second pass happens whenever orphan nodes are chained over two or more levels.
Added ->clear('bounded') so each pass only binds its own values.
IndexerController::sendResponse() passed the exception code straight to the HTTP status header. Database exceptions have code 0, which sent Status: 0; the web server rejects it and the real error message is lost. The new IndexerController::getHttpStatus() now falls back to 500 when the code isn't an HTTP error status (400–599).
Added unit tests: TaxonomyTest checks that every pass binds only its own values, and IndexerControllerTest checks the status code mapping.
Testing Instructions
Use a database whose #__finder_taxonomy table has orphan nodes chained over several levels (leaf nodes with no entry in #__finder_taxonomy_map, whose parent_id points to another such node).
Go to Smart Search → Index → Optimise (or request index.php?option=com_finder&task=indexer.optimize&format=json with a valid token).
Run ./libraries/vendor/bin/phpunit tests/Unit/Component/Finder for the unit tests.
Actual result BEFORE applying this Pull Request
Optimise fails with ExecutionFailureException: HY093 and the orphan nodes are only partly removed. The web server rejects the Status: 0 header, so the browser gets no usable error message.
Expected result AFTER applying this Pull Request
All orphan nodes are removed over as many passes as needed, and Optimise ends with "Indexing Complete". Any remaining exception is sent with HTTP 500 and its real message.