From 9d1d0e5df9d91d5b18bc62c277c6baff5faee69c Mon Sep 17 00:00:00 2001 From: Nick Shanin Date: Fri, 23 Oct 2026 00:27:01 +0000 Subject: [PATCH 1/5] SOLR-16155: DocumentBuilder should not include field values in error messages --- ...16155-no-field-values-in-update-errors.yml | 7 +++ .../solr/handler/loader/JavabinLoader.java | 14 ++++- .../apache/solr/schema/DenseVectorField.java | 7 ++- .../org/apache/solr/schema/FieldType.java | 7 ++- .../org/apache/solr/schema/RankField.java | 10 ++- .../StrFloatLateInteractionVectorField.java | 7 ++- .../apache/solr/update/DocumentBuilder.java | 61 ++++++++----------- .../handler/loader/JavabinLoaderTest.java | 42 +++++++++++++ .../solr/schema/DenseVectorFieldTest.java | 58 +++++++++++++++++- .../org/apache/solr/schema/RankFieldTest.java | 11 ++++ .../solr/update/DocumentBuilderTest.java | 39 +++++++++--- .../TolerantUpdateProcessorTest.java | 12 ++-- 12 files changed, 209 insertions(+), 66 deletions(-) create mode 100644 changelog/unreleased/SOLR-16155-no-field-values-in-update-errors.yml diff --git a/changelog/unreleased/SOLR-16155-no-field-values-in-update-errors.yml b/changelog/unreleased/SOLR-16155-no-field-values-in-update-errors.yml new file mode 100644 index 000000000000..4eb1c6bbf989 --- /dev/null +++ b/changelog/unreleased/SOLR-16155-no-field-values-in-update-errors.yml @@ -0,0 +1,7 @@ +title: Error messages for a failed document add no longer include the field values or the document contents, only the document id and the field name; the error messages raised while creating a field from a value are covered as well, and the values are logged at TRACE level instead +type: changed +authors: + - name: Nick Shanin +links: + - name: SOLR-16155 + url: https://issues.apache.org/jira/browse/SOLR-16155 diff --git a/solr/core/src/java/org/apache/solr/handler/loader/JavabinLoader.java b/solr/core/src/java/org/apache/solr/handler/loader/JavabinLoader.java index 23ad278d4918..a0591637b557 100644 --- a/solr/core/src/java/org/apache/solr/handler/loader/JavabinLoader.java +++ b/solr/core/src/java/org/apache/solr/handler/loader/JavabinLoader.java @@ -38,6 +38,7 @@ import org.apache.solr.common.util.NamedList; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.schema.SchemaField; import org.apache.solr.update.AddUpdateCommand; import org.apache.solr.update.DeleteUpdateCommand; import org.apache.solr.update.processor.UpdateRequestProcessor; @@ -123,7 +124,9 @@ public void update( addCmd.clear(); } catch (IOException e) { throw new SolrException( - SolrException.ErrorCode.SERVER_ERROR, "ERROR adding document " + document, e); + SolrException.ErrorCode.SERVER_ERROR, + "ERROR adding document" + describeDocument(req, document), + e); } } }; @@ -141,6 +144,15 @@ public void update( } } + /** The document's unique key for an error message, without any other field values. */ + private static String describeDocument(SolrQueryRequest req, SolrInputDocument document) { + SchemaField uniqueKeyField = req.getSchema().getUniqueKeyField(); + if (uniqueKeyField == null) { + return ""; + } + return " [doc=" + document.getFieldValue(uniqueKeyField.getName()) + "]"; + } + @SuppressWarnings( "ReferenceEquality") // END_OBJ is a unique sentinel; identity check is intentional private void handleMultiStream( diff --git a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java index e06dfa6127b1..af7509a615b6 100644 --- a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java +++ b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java @@ -368,10 +368,11 @@ public List createFields(SchemaField field, Object value) { } return fields; } catch (RuntimeException e) { + if (log.isTraceEnabled()) { + log.trace("Error while creating field '{}' from value '{}'", field, value, e); + } throw new SolrException( - SolrException.ErrorCode.SERVER_ERROR, - "Error while creating field '" + field + "' from value '" + value + "'", - e); + SolrException.ErrorCode.SERVER_ERROR, "Error while creating field '" + field + "'", e); } } diff --git a/solr/core/src/java/org/apache/solr/schema/FieldType.java b/solr/core/src/java/org/apache/solr/schema/FieldType.java index ce4dc99d92a3..c45285acbd73 100644 --- a/solr/core/src/java/org/apache/solr/schema/FieldType.java +++ b/solr/core/src/java/org/apache/solr/schema/FieldType.java @@ -306,10 +306,11 @@ public IndexableField createField(SchemaField field, Object value) { } catch (SolrException se) { throw se; // BAD_REQUEST to fall through } catch (RuntimeException e) { + if (log.isTraceEnabled()) { + log.trace("Error while creating field '{}' from value '{}'", field, value, e); + } throw new SolrException( - SolrException.ErrorCode.SERVER_ERROR, - "Error while creating field '" + field + "' from value '" + value + "'", - e); + SolrException.ErrorCode.SERVER_ERROR, "Error while creating field '" + field + "'", e); } if (val == null) return null; diff --git a/solr/core/src/java/org/apache/solr/schema/RankField.java b/solr/core/src/java/org/apache/solr/schema/RankField.java index 0bb1b09db193..9b75d86a8dd8 100644 --- a/solr/core/src/java/org/apache/solr/schema/RankField.java +++ b/solr/core/src/java/org/apache/solr/schema/RankField.java @@ -17,6 +17,7 @@ package org.apache.solr.schema; import java.io.IOException; +import java.lang.invoke.MethodHandles; import java.util.Map; import org.apache.lucene.document.FeatureField; import org.apache.lucene.index.IndexableField; @@ -30,6 +31,8 @@ import org.apache.solr.search.QParser; import org.apache.solr.search.RankQParserPlugin; import org.apache.solr.uninverting.UninvertingReader.Type; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * {@code RankField}s can be used to store scoring factors to improve document ranking. They should @@ -59,6 +62,8 @@ */ public class RankField extends FieldType { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + /* * While the user can create multiple RankFields, internally we use a single Lucene field, * and we map the Solr field name to the "feature" in Lucene's FeatureField. This is mainly @@ -109,9 +114,12 @@ protected IndexableField createField(String name, String val, IndexableFieldType try { featureValue = Float.parseFloat(val); } catch (NumberFormatException nfe) { + if (log.isTraceEnabled()) { + log.trace("Error while creating field '{}' from value '{}'", name, val, nfe); + } throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, - "Error while creating field '" + name + "' from value '" + val + "'. Expecting float.", + "Error while creating field '" + name + "'. Expecting float.", nfe); } // Internally, we always use the same field diff --git a/solr/core/src/java/org/apache/solr/schema/StrFloatLateInteractionVectorField.java b/solr/core/src/java/org/apache/solr/schema/StrFloatLateInteractionVectorField.java index f80b6cbea54b..e05a19d6d28d 100644 --- a/solr/core/src/java/org/apache/solr/schema/StrFloatLateInteractionVectorField.java +++ b/solr/core/src/java/org/apache/solr/schema/StrFloatLateInteractionVectorField.java @@ -209,10 +209,11 @@ public List createFields(SchemaField field, Object value) { return fields; } catch (SyntaxError | RuntimeException e) { + if (log.isTraceEnabled()) { + log.trace("Error while creating field '{}' from value '{}'", field, value, e); + } throw new SolrException( - SolrException.ErrorCode.SERVER_ERROR, - "Error while creating field '" + field + "' from value '" + value + "'", - e); + SolrException.ErrorCode.SERVER_ERROR, "Error while creating field '" + field + "'", e); } } diff --git a/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java b/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java index 390d86161812..a14389bc4106 100644 --- a/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java +++ b/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java @@ -16,6 +16,7 @@ */ package org.apache.solr.update; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -34,14 +35,17 @@ import org.apache.solr.schema.FieldType; import org.apache.solr.schema.IndexSchema; import org.apache.solr.schema.SchemaField; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** Builds a Lucene {@link Document} from a {@link SolrInputDocument}. */ public class DocumentBuilder { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + // accessible only for tests static int MIN_LENGTH_TO_MOVE_LAST = Integer.getInteger("solr.docBuilder.minLengthToMoveLast", 4 * 1024); // internal setting - static int MAX_VALUES_AS_STRING_LENGTH = 256; /** * Add a field value to a given document. @@ -93,6 +97,18 @@ private static String getID(SolrInputDocument doc, IndexSchema schema) { return id; } + private static String getFieldErrorMessage( + SolrInputDocument doc, IndexSchema schema, String fieldName) { + return "ERROR: " + getID(doc, schema) + "Error adding field '" + fieldName + "'"; + } + + /** The error message omits field values, so keep them available for debugging at TRACE. */ + private static void traceFieldError(String message, SolrInputField field, Exception ex) { + if (log.isTraceEnabled()) { + log.trace("{}: value={}", message, field.getValue(), ex); + } + } + /** * @see DocumentBuilder#toDocument(SolrInputDocument, IndexSchema, boolean, boolean) */ @@ -163,21 +179,12 @@ public static Document toDocument( && field.getValueCount() > 1 && !(sfield.getType() instanceof DenseVectorField)) { - // Ensure we do not flood the logs with extremely long values - String fieldValue = field.getValue().toString(); - if (fieldValue.length() > MAX_VALUES_AS_STRING_LENGTH) { - assert fieldValue.endsWith("]"); - fieldValue = fieldValue.substring(0, MAX_VALUES_AS_STRING_LENGTH - 4) + "...]"; - } - throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "ERROR: " + getID(doc, schema) + "multiple values encountered for non multiValued field " - + sfield.getName() - + ": " - + fieldValue); + + sfield.getName()); } List copyFields = schema.getCopyFieldsList(name); @@ -272,29 +279,13 @@ public static Document toDocument( } } } catch (SolrException ex) { - throw new SolrException( - SolrException.ErrorCode.getErrorCode(ex.code()), - "ERROR: " - + getID(doc, schema) - + "Error adding field '" - + field.getName() - + "'='" - + field.getValue() - + "' msg=" - + ex.getMessage(), - ex); + String message = getFieldErrorMessage(doc, schema, name); + traceFieldError(message, field, ex); + throw new SolrException(SolrException.ErrorCode.getErrorCode(ex.code()), message, ex); } catch (Exception ex) { - throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, - "ERROR: " - + getID(doc, schema) - + "Error adding field '" - + field.getName() - + "'='" - + field.getValue() - + "' msg=" - + ex.getMessage(), - ex); + String message = getFieldErrorMessage(doc, schema, name); + traceFieldError(message, field, ex); + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message, ex); } // make sure the field was used somehow... @@ -376,9 +367,7 @@ private static boolean addCopyFields( throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Multiple values encountered for non multiValued copy field " - + destinationField.getName() - + ": " - + originalFieldValue); + + destinationField.getName()); } Object fieldValue = originalFieldValue; // Perhaps trim the length of a copy field diff --git a/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java b/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java index 66ac7c724db4..c17631a11638 100644 --- a/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java +++ b/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java @@ -17,6 +17,7 @@ package org.apache.solr.handler.loader; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -24,12 +25,14 @@ import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer; import org.apache.solr.client.solrj.request.JavaBinUpdateRequestCodec; import org.apache.solr.client.solrj.request.UpdateRequest; +import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.update.AddUpdateCommand; import org.apache.solr.update.processor.BufferingRequestProcessor; +import org.apache.solr.update.processor.UpdateRequestProcessor; import org.junit.BeforeClass; import org.junit.Test; @@ -115,4 +118,43 @@ public void javabinLoader_denseVector_shouldIndexCorrectly() throws Exception { "//result/doc[1]/arr[@name=\"vector\"]/float[3][.='" + 3.4 + "']", "//result/doc[1]/arr[@name=\"vector\"]/float[4][.='" + 4.4 + "']"); } + + @Test + public void testIOExceptionDoesNotLeakDocumentContents() throws Exception { + SolrInputDocument doc = new SolrInputDocument(); + doc.addField("id", "7"); + doc.addField("name", "secret customer value"); + + UpdateRequest updateRequest = new UpdateRequest(); + updateRequest.add(doc); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + new JavaBinUpdateRequestCodec().marshal(updateRequest, os); + + UpdateRequestProcessor failingProcessor = + new BufferingRequestProcessor(null) { + @Override + public void processAdd(AddUpdateCommand cmd) throws IOException { + throw new IOException("simulated failure for secret customer value"); + } + }; + + SolrQueryRequest req = req(); + try { + SolrException ex = + expectThrows( + SolrException.class, + () -> + new JavabinLoader() + .load( + req, + new SolrQueryResponse(), + new ContentStreamBase.ByteArrayStream(os.toByteArray(), "test"), + failingProcessor)); + assertEquals("ERROR adding document [doc=7]", ex.getMessage()); + assertFalse(ex.getMessage().contains("secret customer value")); + } finally { + req.close(); + } + } } diff --git a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java index 3160b4561150..1439f3235c1d 100644 --- a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java @@ -21,6 +21,7 @@ import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.lucene.index.VectorEncoding; @@ -728,7 +729,7 @@ public void denseVectorByteEncoding_shouldRaiseExceptionWithValuesOutsideBoundar assertThat( thrown.getCause().getMessage(), is( - "Error while creating field 'vector_byte_encoding{type=knn_vector_byte_encoding,properties=indexed,stored,omitNorms,omitTermFreqAndPositions,useDocValuesAsStored}' from value '[128, 6, 7, 8]'")); + "Error while creating field 'vector_byte_encoding{type=knn_vector_byte_encoding,properties=indexed,stored,omitNorms,omitTermFreqAndPositions,useDocValuesAsStored}'")); assertThat( thrown.getCause().getCause().getMessage(), @@ -750,7 +751,7 @@ public void denseVectorByteEncoding_shouldRaiseExceptionWithValuesOutsideBoundar assertThat( thrown.getCause().getMessage(), is( - "Error while creating field 'vector_byte_encoding{type=knn_vector_byte_encoding,properties=indexed,stored,omitNorms,omitTermFreqAndPositions,useDocValuesAsStored}' from value '[1, -129, 7, 8]'")); + "Error while creating field 'vector_byte_encoding{type=knn_vector_byte_encoding,properties=indexed,stored,omitNorms,omitTermFreqAndPositions,useDocValuesAsStored}'")); assertThat( thrown.getCause().getCause().getMessage(), is( @@ -760,6 +761,57 @@ public void denseVectorByteEncoding_shouldRaiseExceptionWithValuesOutsideBoundar } } + @Test + public void fieldTypeCreateFieldErrorDoesNotLeakFieldValue() throws Exception { + try { + initCore("solrconfig.xml", "schema-densevector.xml"); + IndexSchema schema = h.getCore().getLatestSchema(); + // A field type whose conversion fails with a RuntimeException, so the failure surfaces + // through FieldType.createField's error wrapping. + FieldType failingType = + new StrField() { + @Override + public String toInternal(String val) { + throw new IllegalArgumentException("cannot convert: " + val); + } + }; + failingType.init(schema, new HashMap<>()); + SchemaField field = + new SchemaField( + "failing_field", failingType, FieldProperties.INDEXED | FieldProperties.STORED, null); + SolrException thrown = + assertThrows( + "A failing conversion should throw an exception", + SolrException.class, + () -> failingType.createField(field, "secret-value")); + assertTrue(thrown.getMessage().contains("Error while creating field")); + assertFalse(thrown.getMessage().contains("secret-value")); + } finally { + deleteCore(); + } + } + + @Test + public void denseVectorCreateFieldsErrorDoesNotLeakFieldValue() throws Exception { + try { + initCore("solrconfig.xml", "schema-densevector.xml"); + SolrInputDocument doc = new SolrInputDocument(); + doc.addField("id", "0"); + doc.addField("vector_byte_encoding", Arrays.asList(128, 6, 7, 8)); + RuntimeException thrown = + assertThrows( + "Incorrect elements should throw an exception", + SolrException.class, + () -> { + assertU(adoc(doc)); + }); + assertTrue(thrown.getCause().getMessage().contains("Error while creating field")); + assertFalse(thrown.getCause().getMessage().contains("[128, 6, 7, 8]")); + } finally { + deleteCore(); + } + } + @Test public void denseVectorByteEncoding_shouldRaiseExceptionWithFloatValues() throws Exception { try { @@ -779,7 +831,7 @@ public void denseVectorByteEncoding_shouldRaiseExceptionWithFloatValues() throws assertThat( thrown.getCause().getMessage(), is( - "Error while creating field 'vector_byte_encoding{type=knn_vector_byte_encoding,properties=indexed,stored,omitNorms,omitTermFreqAndPositions,useDocValuesAsStored}' from value '[14.3, 6.2, 7.2, 8.1]'")); + "Error while creating field 'vector_byte_encoding{type=knn_vector_byte_encoding,properties=indexed,stored,omitNorms,omitTermFreqAndPositions,useDocValuesAsStored}'")); assertThat( thrown.getCause().getCause().getMessage(), diff --git a/solr/core/src/test/org/apache/solr/schema/RankFieldTest.java b/solr/core/src/test/org/apache/solr/schema/RankFieldTest.java index 2a127c5441b0..a34d6be842b7 100644 --- a/solr/core/src/test/org/apache/solr/schema/RankFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/RankFieldTest.java @@ -22,6 +22,7 @@ import org.apache.lucene.index.LeafReader; import org.apache.lucene.util.BytesRef; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrException; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.util.LogListener; @@ -59,6 +60,16 @@ public void testBasic() { RankField.class, h.getCore().getLatestSchema().getField(RANK_1).getType().getClass()); } + public void testCreateFieldErrorDoesNotLeakFieldValue() { + RankField type = (RankField) h.getCore().getLatestSchema().getField(RANK_1).getType(); + SolrException ex = + expectThrows( + SolrException.class, () -> type.createField(RANK_1, "not-a-float-secret", null)); + assertTrue(ex.getMessage().contains("Error while creating field '" + RANK_1 + "'")); + assertTrue(ex.getMessage().contains("Expecting float.")); + assertFalse(ex.getMessage().contains("not-a-float-secret")); + } + public void testBadFormat() { try (LogListener errors = LogListener.error(RequestHandlerBase.class).substring("Expecting float")) { diff --git a/solr/core/src/test/org/apache/solr/update/DocumentBuilderTest.java b/solr/core/src/test/org/apache/solr/update/DocumentBuilderTest.java index 80ecd512e1c9..51b0e2c27b70 100644 --- a/solr/core/src/test/org/apache/solr/update/DocumentBuilderTest.java +++ b/solr/core/src/test/org/apache/solr/update/DocumentBuilderTest.java @@ -111,6 +111,35 @@ public void testExceptions() { DocumentBuilder.toDocument(doc, core.getLatestSchema()); } + @Test + public void testExceptionMessagesDoNotLeakFieldValues() { + SolrCore core = h.getCore(); + + SolrInputDocument doc = new SolrInputDocument(); + doc.addField("id", "123"); + doc.addField("weight", "not-a-number-secret"); + SolrException ex = + expectThrows( + SolrException.class, () -> DocumentBuilder.toDocument(doc, core.getLatestSchema())); + assertTrue(ex.getMessage().contains("Error adding field 'weight'")); + assertFalse(ex.getMessage().contains("not-a-number-secret")); + + SolrInputDocument multiValueDoc = new SolrInputDocument(); + multiValueDoc.addField("id", "456"); + multiValueDoc.addField("weight", "secret-one"); + multiValueDoc.addField("weight", "secret-two"); + SolrException multiValueEx = + expectThrows( + SolrException.class, + () -> DocumentBuilder.toDocument(multiValueDoc, core.getLatestSchema())); + assertTrue( + multiValueEx + .getMessage() + .contains("multiple values encountered for non multiValued field weight")); + assertFalse(multiValueEx.getMessage().contains("secret-one")); + assertFalse(multiValueEx.getMessage().contains("secret-two")); + } + @Test public void testMultiField() { SolrCore core = h.getCore(); @@ -417,10 +446,7 @@ public void denseVector_incorrectCopyFieldDestinationType_shouldThrowException() () -> { DocumentBuilder.toDocument(doc, core.getLatestSchema()); }); - assertThat( - thrown.getMessage(), - is( - "ERROR: [doc=0] Error adding field 'vector3'='[1.1, 2.1, 3.1, 4.1]' msg=The copy field destination must be a DenseVectorField: vector_f_p")); + assertThat(thrown.getMessage(), is("ERROR: [doc=0] Error adding field 'vector3'")); } @Test @@ -438,10 +464,7 @@ public void denseVector_incorrectCopyFieldDestinationDimension_shouldThrowExcept () -> { DocumentBuilder.toDocument(doc, core.getLatestSchema()); }); - assertThat( - thrown.getMessage(), - is( - "ERROR: [doc=0] Error adding field 'vector4'='[1.1, 2.1, 3.1, 4.1]' msg=Error while creating field 'vector5{type=knn_vector5,properties=indexed,stored}' from value '[1.1, 2.1, 3.1, 4.1]'")); + assertThat(thrown.getMessage(), is("ERROR: [doc=0] Error adding field 'vector4'")); assertThat( thrown.getCause().getCause().getMessage(), is( diff --git a/solr/core/src/test/org/apache/solr/update/processor/TolerantUpdateProcessorTest.java b/solr/core/src/test/org/apache/solr/update/processor/TolerantUpdateProcessorTest.java index f87d1b2d260a..b2f4bc93799a 100644 --- a/solr/core/src/test/org/apache/solr/update/processor/TolerantUpdateProcessorTest.java +++ b/solr/core/src/test/org/apache/solr/update/processor/TolerantUpdateProcessorTest.java @@ -237,10 +237,8 @@ public void testMaxErrorsThrowsException() { assertAddsSucceedWithErrors( "tolerant-chain-max-errors-not-set", docs, requestParams, badIds)); assertTrue( - e.getMessage(), - e.getMessage() - .contains( - "ERROR: [doc=1] Error adding field 'weight'='b' msg=For input string: \"b\"")); + e.getMessage(), e.getMessage().contains("ERROR: [doc=1] Error adding field 'weight'")); + assertFalse(e.getMessage(), e.getMessage().contains("'b'")); // the first good documents made it to the index assertU(commit()); assertQ(req("q", "*:*"), "//result[@numFound='6']"); @@ -269,10 +267,8 @@ public void testMaxErrors0() { () -> assertAddsSucceedWithErrors( "tolerant-chain-max-errors-10", smallBatch, requestParams, "1")); - assertTrue( - e.getMessage() - .contains( - "ERROR: [doc=1] Error adding field 'weight'='b' msg=For input string: \"b\"")); + assertTrue(e.getMessage().contains("ERROR: [doc=1] Error adding field 'weight'")); + assertFalse(e.getMessage(), e.getMessage().contains("'b'")); // the first good documents made it to the index assertU(commit()); From 323bafea9e097d05e46f2f088e5034f6fce171ec Mon Sep 17 00:00:00 2001 From: Nick Shanin Date: Fri, 23 Oct 2026 14:02:39 -0400 Subject: [PATCH 2/5] SOLR-16155: keep value-bearing causes out of update error output --- ...16155-no-field-values-in-update-errors.yml | 2 +- .../solr/handler/loader/JavabinLoader.java | 11 +++- .../apache/solr/schema/DenseVectorField.java | 2 +- .../org/apache/solr/schema/FieldType.java | 2 +- .../org/apache/solr/schema/RankField.java | 3 +- .../StrFloatLateInteractionVectorField.java | 2 +- .../apache/solr/update/DocumentBuilder.java | 5 +- .../handler/loader/JavabinLoaderTest.java | 31 +++++++++++ .../solr/schema/DenseVectorFieldTest.java | 38 ++++---------- .../org/apache/solr/schema/RankFieldTest.java | 8 +-- .../solr/update/DocumentBuilderTest.java | 52 +++++++++++++++++-- .../org/apache/solr/util/LogListener.java | 5 ++ 12 files changed, 115 insertions(+), 46 deletions(-) diff --git a/changelog/unreleased/SOLR-16155-no-field-values-in-update-errors.yml b/changelog/unreleased/SOLR-16155-no-field-values-in-update-errors.yml index 4eb1c6bbf989..df880fdc6879 100644 --- a/changelog/unreleased/SOLR-16155-no-field-values-in-update-errors.yml +++ b/changelog/unreleased/SOLR-16155-no-field-values-in-update-errors.yml @@ -1,4 +1,4 @@ -title: Error messages for a failed document add no longer include the field values or the document contents, only the document id and the field name; the error messages raised while creating a field from a value are covered as well, and the values are logged at TRACE level instead +title: Document construction and javabin add error messages omit field values and document contents, retaining the document id and field name where available. Value-bearing causes are omitted from these exceptions so ordinary error logs and response traces do not expose them; conversion and javabin failure details are available at TRACE level instead. type: changed authors: - name: Nick Shanin diff --git a/solr/core/src/java/org/apache/solr/handler/loader/JavabinLoader.java b/solr/core/src/java/org/apache/solr/handler/loader/JavabinLoader.java index a0591637b557..4ae81aadd3b1 100644 --- a/solr/core/src/java/org/apache/solr/handler/loader/JavabinLoader.java +++ b/solr/core/src/java/org/apache/solr/handler/loader/JavabinLoader.java @@ -19,6 +19,7 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; +import java.lang.invoke.MethodHandles; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -42,6 +43,8 @@ import org.apache.solr.update.AddUpdateCommand; import org.apache.solr.update.DeleteUpdateCommand; import org.apache.solr.update.processor.UpdateRequestProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Update handler which uses the JavaBin format @@ -50,6 +53,8 @@ * @see org.apache.solr.common.util.JavaBinCodec */ public class JavabinLoader extends ContentStreamLoader { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + final ContentStreamLoader contentStreamLoader; public JavabinLoader() { @@ -123,10 +128,12 @@ public void update( processor.processAdd(addCmd); addCmd.clear(); } catch (IOException e) { + if (log.isTraceEnabled()) { + log.trace("Error adding document {}", document, e); + } throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, - "ERROR adding document" + describeDocument(req, document), - e); + "ERROR adding document" + describeDocument(req, document)); } } }; diff --git a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java index af7509a615b6..4c570e0ce01f 100644 --- a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java +++ b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java @@ -372,7 +372,7 @@ public List createFields(SchemaField field, Object value) { log.trace("Error while creating field '{}' from value '{}'", field, value, e); } throw new SolrException( - SolrException.ErrorCode.SERVER_ERROR, "Error while creating field '" + field + "'", e); + SolrException.ErrorCode.SERVER_ERROR, "Error while creating field '" + field + "'"); } } diff --git a/solr/core/src/java/org/apache/solr/schema/FieldType.java b/solr/core/src/java/org/apache/solr/schema/FieldType.java index c45285acbd73..6862feb72434 100644 --- a/solr/core/src/java/org/apache/solr/schema/FieldType.java +++ b/solr/core/src/java/org/apache/solr/schema/FieldType.java @@ -310,7 +310,7 @@ public IndexableField createField(SchemaField field, Object value) { log.trace("Error while creating field '{}' from value '{}'", field, value, e); } throw new SolrException( - SolrException.ErrorCode.SERVER_ERROR, "Error while creating field '" + field + "'", e); + SolrException.ErrorCode.SERVER_ERROR, "Error while creating field '" + field + "'"); } if (val == null) return null; diff --git a/solr/core/src/java/org/apache/solr/schema/RankField.java b/solr/core/src/java/org/apache/solr/schema/RankField.java index 9b75d86a8dd8..a794abab09ff 100644 --- a/solr/core/src/java/org/apache/solr/schema/RankField.java +++ b/solr/core/src/java/org/apache/solr/schema/RankField.java @@ -119,8 +119,7 @@ protected IndexableField createField(String name, String val, IndexableFieldType } throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, - "Error while creating field '" + name + "'. Expecting float.", - nfe); + "Error while creating field '" + name + "'. Expecting float."); } // Internally, we always use the same field return new FeatureField(INTERNAL_RANK_FIELD_NAME, name, featureValue); diff --git a/solr/core/src/java/org/apache/solr/schema/StrFloatLateInteractionVectorField.java b/solr/core/src/java/org/apache/solr/schema/StrFloatLateInteractionVectorField.java index e05a19d6d28d..f4f0c07fd6df 100644 --- a/solr/core/src/java/org/apache/solr/schema/StrFloatLateInteractionVectorField.java +++ b/solr/core/src/java/org/apache/solr/schema/StrFloatLateInteractionVectorField.java @@ -213,7 +213,7 @@ public List createFields(SchemaField field, Object value) { log.trace("Error while creating field '{}' from value '{}'", field, value, e); } throw new SolrException( - SolrException.ErrorCode.SERVER_ERROR, "Error while creating field '" + field + "'", e); + SolrException.ErrorCode.SERVER_ERROR, "Error while creating field '" + field + "'"); } } diff --git a/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java b/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java index a14389bc4106..202ca2c44809 100644 --- a/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java +++ b/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java @@ -281,11 +281,12 @@ public static Document toDocument( } catch (SolrException ex) { String message = getFieldErrorMessage(doc, schema, name); traceFieldError(message, field, ex); - throw new SolrException(SolrException.ErrorCode.getErrorCode(ex.code()), message, ex); + // Causes can contain field values and are logged and serialized by request handlers. + throw new SolrException(SolrException.ErrorCode.getErrorCode(ex.code()), message); } catch (Exception ex) { String message = getFieldErrorMessage(doc, schema, name); traceFieldError(message, field, ex); - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message, ex); + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message); } // make sure the field was used somehow... diff --git a/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java b/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java index c17631a11638..b0bdc377ab27 100644 --- a/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java +++ b/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java @@ -18,6 +18,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -28,13 +30,19 @@ import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.util.ContentStreamBase; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.Utils; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.servlet.ResponseUtils; import org.apache.solr.update.AddUpdateCommand; import org.apache.solr.update.processor.BufferingRequestProcessor; import org.apache.solr.update.processor.UpdateRequestProcessor; +import org.apache.solr.util.LogLevel; +import org.apache.solr.util.LogListener; import org.junit.BeforeClass; import org.junit.Test; +import org.slf4j.LoggerFactory; public class JavabinLoaderTest extends SolrTestCaseJ4 { @BeforeClass @@ -120,6 +128,7 @@ public void javabinLoader_denseVector_shouldIndexCorrectly() throws Exception { } @Test + @LogLevel("org.apache.solr.handler.loader.JavabinLoader=INFO") public void testIOExceptionDoesNotLeakDocumentContents() throws Exception { SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", "7"); @@ -153,6 +162,28 @@ public void processAdd(AddUpdateCommand cmd) throws IOException { failingProcessor)); assertEquals("ERROR adding document [doc=7]", ex.getMessage()); assertFalse(ex.getMessage().contains("secret customer value")); + assertEquals(500, ex.code()); + + try (LogListener errors = LogListener.error(JavabinLoader.class)) { + var logger = LoggerFactory.getLogger(JavabinLoader.class); + NamedList errorInfo = new NamedList<>(); + assertEquals(500, ResponseUtils.getErrorInfo(ex, errorInfo, logger, false)); + assertNotNull("Exercise response traces, not just the outer message", errorInfo.get("trace")); + assertFalse(Utils.toJSONString(errorInfo).contains("secret customer value")); + var typedErrorInfo = ResponseUtils.getTypedErrorInfo(ex, logger, false); + assertNotNull(typedErrorInfo.trace); + assertFalse(Utils.toJSONString(typedErrorInfo).contains("secret customer value")); + assertEquals(2, errors.getCount()); + while (!errors.getQueue().isEmpty()) { + var event = errors.getQueue().poll(); + assertNotNull(event.getThrown()); + StringWriter trace = new StringWriter(); + event.getThrown().printStackTrace(new PrintWriter(trace)); + assertTrue(trace.toString().contains("ERROR adding document [doc=7]")); + assertFalse(trace.toString().contains("secret customer value")); + } + } + assertNull(ex.getCause()); } finally { req.close(); } diff --git a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java index 1439f3235c1d..46b99f31ec8b 100644 --- a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java @@ -726,15 +726,8 @@ public void denseVectorByteEncoding_shouldRaiseExceptionWithValuesOutsideBoundar assertU(adoc(doc)); }); - assertThat( - thrown.getCause().getMessage(), - is( - "Error while creating field 'vector_byte_encoding{type=knn_vector_byte_encoding,properties=indexed,stored,omitNorms,omitTermFreqAndPositions,useDocValuesAsStored}'")); - - assertThat( - thrown.getCause().getCause().getMessage(), - is( - "incorrect vector element: '128'. The expected format is:'[b1,b2..b3]' where each element b is a byte (-128 to 127)")); + assertThat(thrown.getMessage(), is("ERROR: [doc=0] Error adding field 'vector_byte_encoding'")); + assertNull(thrown.getCause()); SolrInputDocument doc1 = new SolrInputDocument(); doc1.addField("id", "1"); @@ -748,14 +741,8 @@ public void denseVectorByteEncoding_shouldRaiseExceptionWithValuesOutsideBoundar assertU(adoc(doc1)); }); - assertThat( - thrown.getCause().getMessage(), - is( - "Error while creating field 'vector_byte_encoding{type=knn_vector_byte_encoding,properties=indexed,stored,omitNorms,omitTermFreqAndPositions,useDocValuesAsStored}'")); - assertThat( - thrown.getCause().getCause().getMessage(), - is( - "incorrect vector element: '-129'. The expected format is:'[b1,b2..b3]' where each element b is a byte (-128 to 127)")); + assertThat(thrown.getMessage(), is("ERROR: [doc=1] Error adding field 'vector_byte_encoding'")); + assertNull(thrown.getCause()); } finally { deleteCore(); } @@ -786,6 +773,8 @@ public String toInternal(String val) { () -> failingType.createField(field, "secret-value")); assertTrue(thrown.getMessage().contains("Error while creating field")); assertFalse(thrown.getMessage().contains("secret-value")); + assertEquals(500, thrown.code()); + assertNull(thrown.getCause()); } finally { deleteCore(); } @@ -805,8 +794,8 @@ public void denseVectorCreateFieldsErrorDoesNotLeakFieldValue() throws Exception () -> { assertU(adoc(doc)); }); - assertTrue(thrown.getCause().getMessage().contains("Error while creating field")); - assertFalse(thrown.getCause().getMessage().contains("[128, 6, 7, 8]")); + assertThat(thrown.getMessage(), is("ERROR: [doc=0] Error adding field 'vector_byte_encoding'")); + assertNull(thrown.getCause()); } finally { deleteCore(); } @@ -828,15 +817,8 @@ public void denseVectorByteEncoding_shouldRaiseExceptionWithFloatValues() throws assertU(adoc(doc)); }); - assertThat( - thrown.getCause().getMessage(), - is( - "Error while creating field 'vector_byte_encoding{type=knn_vector_byte_encoding,properties=indexed,stored,omitNorms,omitTermFreqAndPositions,useDocValuesAsStored}'")); - - assertThat( - thrown.getCause().getCause().getMessage(), - is( - "incorrect vector element: '14.3'. The expected format is:'[b1,b2..b3]' where each element b is a byte (-128 to 127)")); + assertThat(thrown.getMessage(), is("ERROR: [doc=0] Error adding field 'vector_byte_encoding'")); + assertNull(thrown.getCause()); } finally { deleteCore(); } diff --git a/solr/core/src/test/org/apache/solr/schema/RankFieldTest.java b/solr/core/src/test/org/apache/solr/schema/RankFieldTest.java index a34d6be842b7..b3edb4f51930 100644 --- a/solr/core/src/test/org/apache/solr/schema/RankFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/RankFieldTest.java @@ -68,11 +68,13 @@ public void testCreateFieldErrorDoesNotLeakFieldValue() { assertTrue(ex.getMessage().contains("Error while creating field '" + RANK_1 + "'")); assertTrue(ex.getMessage().contains("Expecting float.")); assertFalse(ex.getMessage().contains("not-a-float-secret")); + assertEquals(400, ex.code()); + assertNull(ex.getCause()); } public void testBadFormat() { try (LogListener errors = - LogListener.error(RequestHandlerBase.class).substring("Expecting float")) { + LogListener.error(RequestHandlerBase.class).substring("Error adding field '" + RANK_1 + "'")) { assertFailedU(adoc("id", "1", RANK_1, "foo")); assertFailedU(adoc("id", "1", RANK_1, "1.2.3")); @@ -81,7 +83,7 @@ public void testBadFormat() { } try (LogListener errors = - LogListener.error(RequestHandlerBase.class).substring("must be finite")) { + LogListener.error(RequestHandlerBase.class).substring("Error adding field '" + RANK_1 + "'")) { assertFailedU(adoc("id", "1", RANK_1, Float.toString(Float.POSITIVE_INFINITY))); assertFailedU(adoc("id", "1", RANK_1, Float.toString(Float.NEGATIVE_INFINITY))); @@ -92,7 +94,7 @@ public void testBadFormat() { } try (LogListener errors = - LogListener.error(RequestHandlerBase.class).substring("must be a positive")) { + LogListener.error(RequestHandlerBase.class).substring("Error adding field '" + RANK_1 + "'")) { assertFailedU(adoc("id", "1", RANK_1, Float.toString(-0.0f))); assertFailedU(adoc("id", "1", RANK_1, Float.toString(-1f))); diff --git a/solr/core/src/test/org/apache/solr/update/DocumentBuilderTest.java b/solr/core/src/test/org/apache/solr/update/DocumentBuilderTest.java index 51b0e2c27b70..2075ae134d17 100644 --- a/solr/core/src/test/org/apache/solr/update/DocumentBuilderTest.java +++ b/solr/core/src/test/org/apache/solr/update/DocumentBuilderTest.java @@ -19,6 +19,8 @@ import static org.hamcrest.core.Is.is; import com.carrotsearch.randomizedtesting.generators.RandomStrings; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -36,7 +38,10 @@ import org.apache.solr.common.SolrInputField; import org.apache.solr.common.util.ByteArrayUtf8CharSequence; import org.apache.solr.core.SolrCore; +import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.schema.FieldType; +import org.apache.solr.util.LogLevel; +import org.apache.solr.util.LogListener; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -104,7 +109,7 @@ public void testExceptions() { expectThrows( Exception.class, () -> DocumentBuilder.toDocument(doc, core.getLatestSchema())); assertTrue("should have document ID", ex.getMessage().indexOf("doc=123") > 0); - assertTrue("cause is number format", ex.getCause() instanceof NumberFormatException); + assertNull("value-bearing parse errors must not escape through causes", ex.getCause()); // now make sure it is OK doc.setField("weight", "1.34"); @@ -123,6 +128,8 @@ public void testExceptionMessagesDoNotLeakFieldValues() { SolrException.class, () -> DocumentBuilder.toDocument(doc, core.getLatestSchema())); assertTrue(ex.getMessage().contains("Error adding field 'weight'")); assertFalse(ex.getMessage().contains("not-a-number-secret")); + assertEquals(400, ex.code()); + assertNull(ex.getCause()); SolrInputDocument multiValueDoc = new SolrInputDocument(); multiValueDoc.addField("id", "456"); @@ -140,6 +147,44 @@ public void testExceptionMessagesDoNotLeakFieldValues() { assertFalse(multiValueEx.getMessage().contains("secret-two")); } + @Test + @LogLevel("org.apache.solr.update.DocumentBuilder=INFO") + public void testFailedUpdateDoesNotLogFieldValue() { + try (LogListener errors = + LogListener.error(RequestHandlerBase.class).substring("Error adding field 'weight'")) { + assertFailedU(adoc("id", "123", "weight", "not-a-number-secret")); + assertEquals(1, errors.getCount()); + var event = errors.getQueue().poll(); + assertNotNull(event); + assertNotNull(event.getThrown()); + StringWriter trace = new StringWriter(); + event.getThrown().printStackTrace(new PrintWriter(trace)); + assertTrue(trace.toString().contains("[doc=123]")); + assertTrue(trace.toString().contains("Error adding field 'weight'")); + assertFalse(trace.toString().contains("not-a-number-secret")); + } + } + + @Test + @LogLevel("org.apache.solr.update.DocumentBuilder=TRACE") + public void testFieldErrorDetailsAreAvailableAtTrace() { + try (LogListener traces = LogListener.trace(DocumentBuilder.class)) { + SolrInputDocument doc = new SolrInputDocument(); + doc.addField("id", "123"); + doc.addField("weight", "not-a-number-secret"); + SolrException ex = + expectThrows( + SolrException.class, + () -> DocumentBuilder.toDocument(doc, h.getCore().getLatestSchema())); + assertNull(ex.getCause()); + assertEquals(1, traces.getCount()); + var event = traces.getQueue().poll(); + assertNotNull(event); + assertTrue(event.getMessage().getFormattedMessage().contains("not-a-number-secret")); + assertTrue(event.getThrown() instanceof NumberFormatException); + } + } + @Test public void testMultiField() { SolrCore core = h.getCore(); @@ -465,9 +510,6 @@ public void denseVector_incorrectCopyFieldDestinationDimension_shouldThrowExcept DocumentBuilder.toDocument(doc, core.getLatestSchema()); }); assertThat(thrown.getMessage(), is("ERROR: [doc=0] Error adding field 'vector4'")); - assertThat( - thrown.getCause().getCause().getMessage(), - is( - "incorrect vector dimension. The vector value has size 4 while it is expected a vector with size 5")); + assertNull(thrown.getCause()); } } diff --git a/solr/test-framework/src/java/org/apache/solr/util/LogListener.java b/solr/test-framework/src/java/org/apache/solr/util/LogListener.java index 5de956d6b434..4299d6efb871 100644 --- a/solr/test-framework/src/java/org/apache/solr/util/LogListener.java +++ b/solr/test-framework/src/java/org/apache/solr/util/LogListener.java @@ -161,6 +161,11 @@ public static LogListener debug(final String logger) { return create(Level.DEBUG, logger); } + /** Listens for TRACE log messages for the specified logger */ + public static LogListener trace(final Class logger) { + return create(Level.TRACE, logger.getName()); + } + // TODO: more factories for other levels? private static LogListener create(final Level level, final String logger) { From df0abc65cfe0a6b8ee0695ee48f50023e7f2814a Mon Sep 17 00:00:00 2001 From: Nick Shanin Date: Fri, 23 Oct 2026 12:33:32 -0600 Subject: [PATCH 3/5] SOLR-16155: gate repair: apply tidy output --- .../solr/handler/loader/JavabinLoaderTest.java | 3 ++- .../org/apache/solr/schema/DenseVectorFieldTest.java | 12 ++++++++---- .../test/org/apache/solr/schema/RankFieldTest.java | 9 ++++++--- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java b/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java index b0bdc377ab27..35a8f8b5669a 100644 --- a/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java +++ b/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java @@ -168,7 +168,8 @@ public void processAdd(AddUpdateCommand cmd) throws IOException { var logger = LoggerFactory.getLogger(JavabinLoader.class); NamedList errorInfo = new NamedList<>(); assertEquals(500, ResponseUtils.getErrorInfo(ex, errorInfo, logger, false)); - assertNotNull("Exercise response traces, not just the outer message", errorInfo.get("trace")); + assertNotNull( + "Exercise response traces, not just the outer message", errorInfo.get("trace")); assertFalse(Utils.toJSONString(errorInfo).contains("secret customer value")); var typedErrorInfo = ResponseUtils.getTypedErrorInfo(ex, logger, false); assertNotNull(typedErrorInfo.trace); diff --git a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java index 46b99f31ec8b..30769fca8a44 100644 --- a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java @@ -726,7 +726,8 @@ public void denseVectorByteEncoding_shouldRaiseExceptionWithValuesOutsideBoundar assertU(adoc(doc)); }); - assertThat(thrown.getMessage(), is("ERROR: [doc=0] Error adding field 'vector_byte_encoding'")); + assertThat( + thrown.getMessage(), is("ERROR: [doc=0] Error adding field 'vector_byte_encoding'")); assertNull(thrown.getCause()); SolrInputDocument doc1 = new SolrInputDocument(); @@ -741,7 +742,8 @@ public void denseVectorByteEncoding_shouldRaiseExceptionWithValuesOutsideBoundar assertU(adoc(doc1)); }); - assertThat(thrown.getMessage(), is("ERROR: [doc=1] Error adding field 'vector_byte_encoding'")); + assertThat( + thrown.getMessage(), is("ERROR: [doc=1] Error adding field 'vector_byte_encoding'")); assertNull(thrown.getCause()); } finally { deleteCore(); @@ -794,7 +796,8 @@ public void denseVectorCreateFieldsErrorDoesNotLeakFieldValue() throws Exception () -> { assertU(adoc(doc)); }); - assertThat(thrown.getMessage(), is("ERROR: [doc=0] Error adding field 'vector_byte_encoding'")); + assertThat( + thrown.getMessage(), is("ERROR: [doc=0] Error adding field 'vector_byte_encoding'")); assertNull(thrown.getCause()); } finally { deleteCore(); @@ -817,7 +820,8 @@ public void denseVectorByteEncoding_shouldRaiseExceptionWithFloatValues() throws assertU(adoc(doc)); }); - assertThat(thrown.getMessage(), is("ERROR: [doc=0] Error adding field 'vector_byte_encoding'")); + assertThat( + thrown.getMessage(), is("ERROR: [doc=0] Error adding field 'vector_byte_encoding'")); assertNull(thrown.getCause()); } finally { deleteCore(); diff --git a/solr/core/src/test/org/apache/solr/schema/RankFieldTest.java b/solr/core/src/test/org/apache/solr/schema/RankFieldTest.java index b3edb4f51930..5155f6954dd4 100644 --- a/solr/core/src/test/org/apache/solr/schema/RankFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/RankFieldTest.java @@ -74,7 +74,8 @@ public void testCreateFieldErrorDoesNotLeakFieldValue() { public void testBadFormat() { try (LogListener errors = - LogListener.error(RequestHandlerBase.class).substring("Error adding field '" + RANK_1 + "'")) { + LogListener.error(RequestHandlerBase.class) + .substring("Error adding field '" + RANK_1 + "'")) { assertFailedU(adoc("id", "1", RANK_1, "foo")); assertFailedU(adoc("id", "1", RANK_1, "1.2.3")); @@ -83,7 +84,8 @@ public void testBadFormat() { } try (LogListener errors = - LogListener.error(RequestHandlerBase.class).substring("Error adding field '" + RANK_1 + "'")) { + LogListener.error(RequestHandlerBase.class) + .substring("Error adding field '" + RANK_1 + "'")) { assertFailedU(adoc("id", "1", RANK_1, Float.toString(Float.POSITIVE_INFINITY))); assertFailedU(adoc("id", "1", RANK_1, Float.toString(Float.NEGATIVE_INFINITY))); @@ -94,7 +96,8 @@ public void testBadFormat() { } try (LogListener errors = - LogListener.error(RequestHandlerBase.class).substring("Error adding field '" + RANK_1 + "'")) { + LogListener.error(RequestHandlerBase.class) + .substring("Error adding field '" + RANK_1 + "'")) { assertFailedU(adoc("id", "1", RANK_1, Float.toString(-0.0f))); assertFailedU(adoc("id", "1", RANK_1, Float.toString(-1f))); From 9ce3e5b4ced0cdfd8348848e2c602adafc1f1f60 Mon Sep 17 00:00:00 2001 From: Nick Shanin Date: Fri, 23 Oct 2026 13:47:24 -0600 Subject: [PATCH 4/5] SOLR-16155: gate repair: add standard test logger --- .../org/apache/solr/handler/loader/JavabinLoaderTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java b/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java index 35a8f8b5669a..b7b9532961cc 100644 --- a/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java +++ b/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -42,9 +43,12 @@ import org.apache.solr.util.LogListener; import org.junit.BeforeClass; import org.junit.Test; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JavabinLoaderTest extends SolrTestCaseJ4 { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + @BeforeClass public static void beforeTests() throws Exception { initCore("solrconfig.xml", "schema.xml"); From 96ed88e6dc17e50df1d1f8f5e8c33ec84529d23b Mon Sep 17 00:00:00 2001 From: Nick Shanin Date: Fri, 23 Oct 2026 14:19:08 -0600 Subject: [PATCH 5/5] SOLR-16155: gate repair: use supported LogListener API --- .../handler/loader/JavabinLoaderTest.java | 20 ++++----- .../solr/update/DocumentBuilderTest.java | 43 +++++++++++-------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java b/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java index b7b9532961cc..078b05c456f7 100644 --- a/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java +++ b/solr/core/src/test/org/apache/solr/handler/loader/JavabinLoaderTest.java @@ -18,8 +18,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Arrays; @@ -168,7 +166,10 @@ public void processAdd(AddUpdateCommand cmd) throws IOException { assertFalse(ex.getMessage().contains("secret customer value")); assertEquals(500, ex.code()); - try (LogListener errors = LogListener.error(JavabinLoader.class)) { + try (LogListener errors = + LogListener.error(JavabinLoader.class).substring("ERROR adding document [doc=7]"); + LogListener leakedValues = + LogListener.error(JavabinLoader.class).substring("secret customer value")) { var logger = LoggerFactory.getLogger(JavabinLoader.class); NamedList errorInfo = new NamedList<>(); assertEquals(500, ResponseUtils.getErrorInfo(ex, errorInfo, logger, false)); @@ -179,14 +180,13 @@ public void processAdd(AddUpdateCommand cmd) throws IOException { assertNotNull(typedErrorInfo.trace); assertFalse(Utils.toJSONString(typedErrorInfo).contains("secret customer value")); assertEquals(2, errors.getCount()); - while (!errors.getQueue().isEmpty()) { - var event = errors.getQueue().poll(); - assertNotNull(event.getThrown()); - StringWriter trace = new StringWriter(); - event.getThrown().printStackTrace(new PrintWriter(trace)); - assertTrue(trace.toString().contains("ERROR adding document [doc=7]")); - assertFalse(trace.toString().contains("secret customer value")); + int leakedValueCount = leakedValues.getCount(); + assertEquals("500 Exception", errors.pollMessage()); + assertEquals("500 Exception", errors.pollMessage()); + while (leakedValues.pollMessage() != null) { + // Drain matches before asserting to avoid close-time secondary failures. } + assertEquals("Sensitive value present in ERROR log", 0, leakedValueCount); } assertNull(ex.getCause()); } finally { diff --git a/solr/core/src/test/org/apache/solr/update/DocumentBuilderTest.java b/solr/core/src/test/org/apache/solr/update/DocumentBuilderTest.java index 2075ae134d17..9a270937c38b 100644 --- a/solr/core/src/test/org/apache/solr/update/DocumentBuilderTest.java +++ b/solr/core/src/test/org/apache/solr/update/DocumentBuilderTest.java @@ -19,8 +19,6 @@ import static org.hamcrest.core.Is.is; import com.carrotsearch.randomizedtesting.generators.RandomStrings; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -151,24 +149,34 @@ public void testExceptionMessagesDoNotLeakFieldValues() { @LogLevel("org.apache.solr.update.DocumentBuilder=INFO") public void testFailedUpdateDoesNotLogFieldValue() { try (LogListener errors = - LogListener.error(RequestHandlerBase.class).substring("Error adding field 'weight'")) { + LogListener.error(RequestHandlerBase.class).substring("Error adding field 'weight'"); + LogListener leakedValues = + LogListener.error(RequestHandlerBase.class).substring("not-a-number-secret"); + LogListener documentIds = + LogListener.error(RequestHandlerBase.class).substring("[doc=123]")) { assertFailedU(adoc("id", "123", "weight", "not-a-number-secret")); assertEquals(1, errors.getCount()); - var event = errors.getQueue().poll(); - assertNotNull(event); - assertNotNull(event.getThrown()); - StringWriter trace = new StringWriter(); - event.getThrown().printStackTrace(new PrintWriter(trace)); - assertTrue(trace.toString().contains("[doc=123]")); - assertTrue(trace.toString().contains("Error adding field 'weight'")); - assertFalse(trace.toString().contains("not-a-number-secret")); + assertEquals(1, documentIds.getCount()); + String message = errors.pollMessage(); + assertNotNull(message); + assertFalse(message.contains("not-a-number-secret")); + assertNotNull(documentIds.pollMessage()); + int leakedValueCount = leakedValues.getCount(); + while (leakedValues.pollMessage() != null) { + // Drain matches before asserting to avoid close-time secondary failures. + } + assertEquals("Sensitive value present in ERROR log", 0, leakedValueCount); } } @Test @LogLevel("org.apache.solr.update.DocumentBuilder=TRACE") public void testFieldErrorDetailsAreAvailableAtTrace() { - try (LogListener traces = LogListener.trace(DocumentBuilder.class)) { + try (LogListener valueTraces = + LogListener.trace(DocumentBuilder.class).substring("not-a-number-secret"); + LogListener exceptionTraces = + LogListener.trace(DocumentBuilder.class) + .substring(NumberFormatException.class.getName())) { SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", "123"); doc.addField("weight", "not-a-number-secret"); @@ -177,11 +185,12 @@ public void testFieldErrorDetailsAreAvailableAtTrace() { SolrException.class, () -> DocumentBuilder.toDocument(doc, h.getCore().getLatestSchema())); assertNull(ex.getCause()); - assertEquals(1, traces.getCount()); - var event = traces.getQueue().poll(); - assertNotNull(event); - assertTrue(event.getMessage().getFormattedMessage().contains("not-a-number-secret")); - assertTrue(event.getThrown() instanceof NumberFormatException); + assertEquals(1, valueTraces.getCount()); + assertEquals(1, exceptionTraces.getCount()); + String traceMessage = valueTraces.pollMessage(); + assertNotNull(traceMessage); + assertTrue(traceMessage.contains("not-a-number-secret")); + assertNotNull(exceptionTraces.pollMessage()); } }