This is an automated email from the ASF dual-hosted git repository.

tballison pushed a commit to branch TIKA-4816-metadata-key-api
in repository https://gitbox.apache.org/repos/asf/tika.git

commit 0e3fe950599a828c9676d22dbcee8ace4b0cca19
Author: tallison <[email protected]>
AuthorDate: Wed Aug 12 11:41:30 2026 -0400

    TIKA-4816 fix EMBEDDED_EXCEPTION erasure under active metadata limiter
---
 .../writefilter/StandardMetadataLimiter.java       |  3 +-
 .../writefilter/StandardMetadataLimiterTest.java   | 75 ++++++++++++++++++++++
 2 files changed, 77 insertions(+), 1 deletion(-)

diff --git 
a/tika-core/src/main/java/org/apache/tika/metadata/writefilter/StandardMetadataLimiter.java
 
b/tika-core/src/main/java/org/apache/tika/metadata/writefilter/StandardMetadataLimiter.java
index 913057a8f0..4acd9db81a 100644
--- 
a/tika-core/src/main/java/org/apache/tika/metadata/writefilter/StandardMetadataLimiter.java
+++ 
b/tika-core/src/main/java/org/apache/tika/metadata/writefilter/StandardMetadataLimiter.java
@@ -82,12 +82,13 @@ public class StandardMetadataLimiter implements 
MetadataWriteLimiter, Serializab
         
ALWAYS_SET_FIELDS.add(AccessPermissions.EXTRACT_FOR_ACCESSIBILITY.getName());
         ALWAYS_SET_FIELDS.add(Metadata.CONTENT_DISPOSITION.getName());
         
ALWAYS_SET_FIELDS.add(TikaCoreProperties.CONTAINER_EXCEPTION.getName());
-        ALWAYS_SET_FIELDS.add(TikaCoreProperties.EMBEDDED_EXCEPTION.getName());
         //Metadata.CONTENT_LOCATION? used by the html parser
     }
 
     static {
         ALWAYS_ADD_FIELDS.add(TikaCoreProperties.TIKA_PARSED_BY.getName());
+        //bag-typed: one embedded-file failure per add() call must not clobber 
prior ones
+        ALWAYS_ADD_FIELDS.add(TikaCoreProperties.EMBEDDED_EXCEPTION.getName());
     }
 
     private static final String METADATA_TRUNCATED_KEY =
diff --git 
a/tika-serialization/src/test/java/org/apache/tika/metadata/writefilter/StandardMetadataLimiterTest.java
 
b/tika-serialization/src/test/java/org/apache/tika/metadata/writefilter/StandardMetadataLimiterTest.java
index 989d1cdf11..9ad85c5fb2 100644
--- 
a/tika-serialization/src/test/java/org/apache/tika/metadata/writefilter/StandardMetadataLimiterTest.java
+++ 
b/tika-serialization/src/test/java/org/apache/tika/metadata/writefilter/StandardMetadataLimiterTest.java
@@ -286,6 +286,81 @@ public class StandardMetadataLimiterTest extends TikaTest {
 
     }
 
+    @Test
+    public void testEmbeddedExceptionAddRetainsAllValues() throws Exception {
+        //EMBEDDED_EXCEPTION is bag-typed and in ALWAYS_ADD_FIELDS: N adds 
must retain
+        //N values, in order, even under an active limiter
+        Metadata metadata = filter(100, 10000, 10000, 100,
+                Collections.EMPTY_SET, Collections.EMPTY_SET, true);
+        metadata.add(TikaCoreProperties.EMBEDDED_EXCEPTION, "exception 1");
+        metadata.add(TikaCoreProperties.EMBEDDED_EXCEPTION, "exception 2");
+        metadata.add(TikaCoreProperties.EMBEDDED_EXCEPTION, "exception 3");
+
+        assertArrayEquals(new String[]{"exception 1", "exception 2", 
"exception 3"},
+                metadata.getValues(TikaCoreProperties.EMBEDDED_EXCEPTION));
+    }
+
+    @Test
+    public void testEmbeddedExceptionSetStillReplaces() throws Exception {
+        //set() must still replace, even though add() now appends
+        Metadata metadata = filter(100, 10000, 10000, 100,
+                Collections.EMPTY_SET, Collections.EMPTY_SET, true);
+        metadata.add(TikaCoreProperties.EMBEDDED_EXCEPTION, "exception 1");
+        metadata.add(TikaCoreProperties.EMBEDDED_EXCEPTION, "exception 2");
+        metadata.set(TikaCoreProperties.EMBEDDED_EXCEPTION, "replacement");
+
+        assertArrayEquals(new String[]{"replacement"},
+                metadata.getValues(TikaCoreProperties.EMBEDDED_EXCEPTION));
+    }
+
+    @Test
+    public void testEmbeddedExceptionPerValueTruncation() throws Exception {
+        //per-value truncation and the truncated-metadata marker must still 
apply,
+        //without erasing values already added
+        Metadata metadata = filter(100, 10, 100000, 100,
+                Collections.EMPTY_SET, Collections.EMPTY_SET, true);
+        metadata.add(TikaCoreProperties.EMBEDDED_EXCEPTION, "short");
+        //800 utf-16 bytes, over the 300-byte always-field floor
+        String longValue = "x".repeat(400);
+        metadata.add(TikaCoreProperties.EMBEDDED_EXCEPTION, longValue);
+
+        String[] values = 
metadata.getValues(TikaCoreProperties.EMBEDDED_EXCEPTION);
+        assertEquals(2, values.length);
+        assertEquals("short", values[0]);
+        //truncated to the 300-byte (utf-16) always-field floor = 150 chars
+        assertEquals(150, values[1].length());
+        assertTruncated(metadata);
+    }
+
+    @Test
+    public void testEmbeddedExceptionSurvivesOverBudget() throws Exception {
+        //always-add fields must not be dropped even once the overall budget 
is exhausted
+        Metadata metadata = filter(100, 1, 1, 100,
+                Collections.EMPTY_SET, Collections.EMPTY_SET, true);
+        metadata.add(TikaCoreProperties.EMBEDDED_EXCEPTION, "e1");
+        metadata.add(TikaCoreProperties.EMBEDDED_EXCEPTION, "e2");
+        metadata.add(TikaCoreProperties.EMBEDDED_EXCEPTION, "e3");
+
+        assertArrayEquals(new String[]{"e1", "e2", "e3"},
+                metadata.getValues(TikaCoreProperties.EMBEDDED_EXCEPTION));
+    }
+
+    @Test
+    public void testSimpleAlwaysSetFieldStillReplacesOnAdd() throws Exception {
+        //CONTAINER_EXCEPTION is SIMPLE (not bag) and stays in 
ALWAYS_SET_FIELDS:
+        //the limiter's add() dispatch must keep replacing, as before this fix.
+        //Use addTrusted() to reach the limiter directly: 
Metadata.add(Property,..) itself
+        //already throws PropertyTypeException on a 2nd add to a SIMPLE 
property, so that
+        //typed path can't otherwise exercise the limiter's repeated-add 
behavior.
+        Metadata metadata = filter(100, 10000, 10000, 100,
+                Collections.EMPTY_SET, Collections.EMPTY_SET, true);
+        metadata.addTrusted(TikaCoreProperties.CONTAINER_EXCEPTION.getName(), 
"container exception 1");
+        metadata.addTrusted(TikaCoreProperties.CONTAINER_EXCEPTION.getName(), 
"container exception 2");
+
+        assertArrayEquals(new String[]{"container exception 2"},
+                metadata.getValues(TikaCoreProperties.CONTAINER_EXCEPTION));
+    }
+
     @Test
     public void testExclude() throws Exception {
         TikaLoader loader = TikaLoader.load(getConfigPath(getClass(), 
"TIKA-3695-exclude.json"));

Reply via email to