SOLR-18175: Return BAD_REQUEST for FieldExistsQuery IllegalStateException#4369
SOLR-18175: Return BAD_REQUEST for FieldExistsQuery IllegalStateException#4369openworld-maker wants to merge 2 commits intoapache:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Updates Solr’s QueryComponent error handling so that FieldExistsQuery execution failures that currently surface as server errors are instead returned as 400 BAD_REQUEST.
Changes:
- Add targeted mapping for
IllegalStateExceptionwhose (possibly nested) message starts withFieldExistsQuery requires..., converting it toSolrException(BAD_REQUEST)in both grouped and ungrouped execution paths. - Introduce a helper method and constant to detect this specific failure signature.
- Add a new unit test class to validate BAD_REQUEST mapping for direct and nested
IllegalStateExceptioncases.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java | Adds the narrow IllegalStateException → BAD_REQUEST remapping for grouped and ungrouped query execution. |
| solr/core/src/test/org/apache/solr/handler/component/QueryComponentFieldExistsQueryErrorHandlingTest.java | Adds tests for the ungrouped execution path to ensure correct exception mapping behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Method method = | ||
| QueryComponent.class.getDeclaredMethod( | ||
| "doProcessUngroupedSearch", ResponseBuilder.class, QueryCommand.class); | ||
| method.setAccessible(true); | ||
| method.invoke(new QueryComponent(), rb, cmd); |
There was a problem hiding this comment.
This test uses reflection with setAccessible(true) to reach a private method, but it isn’t annotated with @SuppressForbidden. The build enables forbidden-apis’ jdk-reflection signatures (see gradle/validation/forbidden-apis.gradle), and similar reflection-based tests in this package suppress it (e.g., AsyncTrackerSemaphoreLeakTest.java:236). Add an appropriate @SuppressForbidden annotation (method or class) with a brief reason to avoid forbidden-apis failures.
There was a problem hiding this comment.
Addressed by replacing the reflection-heavy test entirely with a real request-driven regression test in solr/core/src/test/org/apache/solr/search/FieldExistsQueryBadRequestTest.java; no private-method reflection remains.
| } catch (IllegalStateException e) { | ||
| maybeThrowBadRequestForFieldExistsQueryFailure(e); | ||
| throw e; |
There was a problem hiding this comment.
The PR changes error mapping for the grouped execution path (the groupingSpec != null branch), but the added tests only exercise the ungrouped doProcessUngroupedSearch path. Please add a test that triggers the grouped path and asserts the same BAD_REQUEST mapping, to prevent regressions specific to grouped execution.
There was a problem hiding this comment.
copilot is rightfully suspicious.
|
I have tagged @dsmiley for review. I did look at the tests and they appear very "mock heavy", and we tend as a project to try and minimize the use of mocks where possible, b/c many of our bugs occur when real objects interact with other real objects ;-). There are a lot of mock objects in the tests so I asked copilot about this and got this response:
Could you take a stab at reducing/removing the use of mocks? I know QueryComponent can be tough to work with directly because of how it sets up state through ResponseBuilders etc... If a non mock version isn't feasible, then it's better to have this test, even if it's brittle! |
dsmiley
left a comment
There was a problem hiding this comment.
It'd be nice to see if this is fixed in the context of a facet.query as well.
| */ | ||
| public class QueryComponent extends SearchComponent { | ||
| public static final String COMPONENT_NAME = "query"; | ||
| private static final String FIELD_EXISTS_QUERY_REQUIRES_PREFIX = "FieldExistsQuery requires"; |
There was a problem hiding this comment.
just inline this please; the constant isn't useful
| import org.junit.Test; | ||
| import org.mockito.Mockito; | ||
|
|
||
| public class QueryComponentFieldExistsQueryErrorHandlingTest extends SolrTestCaseJ4 { |
There was a problem hiding this comment.
I don't trust Mocking to reflect the real world we want, not to mention it can be rather invasive as this test shows. And invasiveness leads to a bigger maintenance burden over time. Can you not provoke the problem as reported in a realistic test? I can try to help if needed or we just provoke it in a manual way and not even have an automated test. It's questionable if the problem is worth an extensive test.
There was a problem hiding this comment.
Addressed by replacing the mock-based approach with a realistic regression test in solr/core/src/test/org/apache/solr/search/FieldExistsQueryBadRequestTest.java. The fix is now validated through normal request handling rather than private-method mocking.
| } catch (IllegalStateException e) { | ||
| maybeThrowBadRequestForFieldExistsQueryFailure(e); | ||
| throw e; |
There was a problem hiding this comment.
copilot is rightfully suspicious.
|
I was hoping for a very different implementation... just modify org.apache.solr.schema.FieldType.getExistenceQuery to have similar checks that FieldExistsQuery has but done here so eagerly at the query parsing stage. Assuming this fixes the problem of course... see if we can provoke what I saw (prompting me to file the issue) |
What this fixes
SOLR-18175 reports that some
FieldExistsQueryfailures are returned as server errors. These should be client errors (400 BAD_REQUEST).Root cause
During query execution,
FieldExistsQuerycan throw anIllegalStateExceptionwith a message starting withFieldExistsQuery requires ...when the field does not support the required data structures. This exception was not being mapped toBAD_REQUESTinQueryComponentexecution paths.Change in this PR
This PR adds a narrow exception mapping in
QueryComponent:If an
IllegalStateException(including nested causes) has a message starting withFieldExistsQuery requires, it is rethrown asSolrException(BAD_REQUEST).All other
IllegalStateExceptions are left unchanged.Tests
Added
QueryComponentFieldExistsQueryErrorHandlingTestcovering:FieldExistsQuery requires ...exception ->BAD_REQUESTFieldExistsQuery requires ...exception ->BAD_REQUESTIllegalStateException-> unchanged behaviorValidation run in Docker included:
TestSolrQueryParserQueryComponent/SearchHandlersuites*QueryComponent*test patternspotlessCheck)