This is an automated email from the ASF dual-hosted git repository.
AlbericByte pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new 4bb735c1d75 fix: return actionable errors for invalid native queries
(#19775)
4bb735c1d75 is described below
commit 4bb735c1d7530a91b349d14ae553c3ee7a4156f4
Author: Frank Chen <[email protected]>
AuthorDate: Tue Jul 28 23:10:52 2026 +0800
fix: return actionable errors for invalid native queries (#19775)
* Return actionable errors for invalid native queries
* Improve nested native query error messages
* test: cover invalid native query error messages
---
.../apache/druid/query/BadJsonQueryException.java | 24 +++++++
.../druid/query/BadJsonQueryExceptionTest.java | 73 ++++++++++++++++++++++
.../org/apache/druid/server/QueryResource.java | 4 ++
.../org/apache/druid/server/QueryResourceTest.java | 18 ++++++
4 files changed, 119 insertions(+)
diff --git
a/processing/src/main/java/org/apache/druid/query/BadJsonQueryException.java
b/processing/src/main/java/org/apache/druid/query/BadJsonQueryException.java
index 56f1a25be89..7722bb70be5 100644
--- a/processing/src/main/java/org/apache/druid/query/BadJsonQueryException.java
+++ b/processing/src/main/java/org/apache/druid/query/BadJsonQueryException.java
@@ -22,6 +22,8 @@ package org.apache.druid.query;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.exc.ValueInstantiationException;
+import com.google.common.base.Strings;
public class BadJsonQueryException extends BadQueryException
{
@@ -32,6 +34,11 @@ public class BadJsonQueryException extends BadQueryException
this(e, JSON_PARSE_ERROR_CODE, e.getMessage(), ERROR_CLASS);
}
+ public BadJsonQueryException(ValueInstantiationException e)
+ {
+ this(e, JSON_PARSE_ERROR_CODE, getErrorMessage(e), e.getClass().getName());
+ }
+
@JsonCreator
private BadJsonQueryException(
@JsonProperty("error") String errorCode,
@@ -51,4 +58,21 @@ public class BadJsonQueryException extends BadQueryException
{
super(cause, errorCode, errorMessage, errorClass, null);
}
+
+ private static String getErrorMessage(ValueInstantiationException e)
+ {
+ Throwable cause = e.getCause();
+ String errorMessage = null;
+ while (cause != null) {
+ if (!Strings.isNullOrEmpty(cause.getMessage())) {
+ errorMessage = cause.getMessage();
+ }
+ cause = cause.getCause();
+ }
+
+ if (errorMessage == null) {
+ return "Invalid native query: the request contains invalid or missing
fields";
+ }
+ return "Invalid native query: " + errorMessage;
+ }
}
diff --git
a/processing/src/test/java/org/apache/druid/query/BadJsonQueryExceptionTest.java
b/processing/src/test/java/org/apache/druid/query/BadJsonQueryExceptionTest.java
new file mode 100644
index 00000000000..2e73eaed002
--- /dev/null
+++
b/processing/src/test/java/org/apache/druid/query/BadJsonQueryExceptionTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.query;
+
+import com.fasterxml.jackson.databind.exc.ValueInstantiationException;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class BadJsonQueryExceptionTest
+{
+ @Test
+ public void testUsesDeepestCauseMessage()
+ {
+ final BadJsonQueryException exception = new BadJsonQueryException(
+ valueInstantiationException(
+ new RuntimeException("intermediate wrapper", new
IllegalArgumentException("actionable validation message"))
+ )
+ );
+
+ Assertions.assertEquals("Invalid native query: actionable validation
message", exception.getMessage());
+ }
+
+ @Test
+ public void testIgnoresEmptyCauseMessage()
+ {
+ final BadJsonQueryException exception = new BadJsonQueryException(
+ valueInstantiationException(
+ new RuntimeException("actionable validation message", new
IllegalArgumentException(""))
+ )
+ );
+
+ Assertions.assertEquals("Invalid native query: actionable validation
message", exception.getMessage());
+ }
+
+ @Test
+ public void testUsesFallbackWithoutCause()
+ {
+ final BadJsonQueryException exception = new
BadJsonQueryException(valueInstantiationException(null));
+
+ Assertions.assertEquals(
+ "Invalid native query: the request contains invalid or missing fields",
+ exception.getMessage()
+ );
+ }
+
+ private static ValueInstantiationException
valueInstantiationException(Throwable cause)
+ {
+ return ValueInstantiationException.from(
+ null,
+ "Jackson wrapper",
+ TypeFactory.defaultInstance().constructType(Query.class),
+ cause
+ );
+ }
+}
diff --git a/server/src/main/java/org/apache/druid/server/QueryResource.java
b/server/src/main/java/org/apache/druid/server/QueryResource.java
index 80ea2895b97..eabf83cfa73 100644
--- a/server/src/main/java/org/apache/druid/server/QueryResource.java
+++ b/server/src/main/java/org/apache/druid/server/QueryResource.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.exc.ValueInstantiationException;
import com.fasterxml.jackson.jaxrs.smile.SmileMediaTypes;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Iterables;
@@ -260,6 +261,9 @@ public class QueryResource implements
QueryCountStatsProvider
try {
baseQuery = ioReaderWriter.getRequestMapper().readValue(in, Query.class);
}
+ catch (ValueInstantiationException e) {
+ throw new BadJsonQueryException(e);
+ }
catch (JsonParseException e) {
throw new BadJsonQueryException(e);
}
diff --git
a/server/src/test/java/org/apache/druid/server/QueryResourceTest.java
b/server/src/test/java/org/apache/druid/server/QueryResourceTest.java
index d7a611bfc32..671c50c5edc 100644
--- a/server/src/test/java/org/apache/druid/server/QueryResourceTest.java
+++ b/server/src/test/java/org/apache/druid/server/QueryResourceTest.java
@@ -22,6 +22,7 @@ package org.apache.druid.server;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.exc.ValueInstantiationException;
import com.fasterxml.jackson.jaxrs.smile.SmileMediaTypes;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
@@ -1043,6 +1044,23 @@ public class QueryResourceTest
Assert.assertEquals(BadJsonQueryException.ERROR_CLASS, e.getErrorClass());
}
+ @Test
+ public void testIncompleteQuery() throws IOException
+ {
+ final Response response = queryResource.doPost(
+ new
ByteArrayInputStream("{\"queryType\":\"scan\"}".getBytes(StandardCharsets.UTF_8)),
+ null /*pretty*/,
+ testServletRequest
+ );
+
+ Assert.assertNotNull(response);
+ Assert.assertEquals(Status.BAD_REQUEST.getStatusCode(),
response.getStatus());
+ final QueryException e = jsonMapper.readValue((byte[])
response.getEntity(), QueryException.class);
+ Assert.assertEquals(QueryException.JSON_PARSE_ERROR_CODE,
e.getErrorCode());
+ Assert.assertEquals(ValueInstantiationException.class.getName(),
e.getErrorClass());
+ Assert.assertEquals("Invalid native query: dataSource can't be null",
e.getMessage());
+ }
+
@Test
public void testResourceLimitExceeded() throws IOException
{
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]