Metadata
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
Baseline date: 2026-10-12. This page was written against Apache Tika 4.1.0 (the latest release on Maven Central on that
date) with the 3.3.x differences noted, Java 21 and later for the examples (Tika 4.x needs Java 17), Spring Boot 4.x on
Spring Framework 7 and the Docker image apache/tika:4.1.0. Metadata is the second output of every parse: the content
handler receives the text, and the Metadata object receives everything else, from the title and author of a document to
the media type Tika detected, the parsers that ran, the encoding it decided on and the warnings it raised. In Tika 4.x
the key names changed more than anything else in the project, so this page spends most of its length on which key to
read and what changed. It explains the Metadata class and typed Property keys, the standards Tika reuses (Dublin
Core, XMP, Office, PDF, TIFF, Message), the tk: namespace and the complete 1.x to 3.x to 4.x mapping, the
reserved-key write guard that makes metadata safer, the KeyPrefix write route, the filters and limiter that shape
output, digests, and a table of the keys that matter for each Bookshelf intake file.
Related pages: Introduction & Architecture (where metadata
sits in the pipeline), Getting Started with the Tika Facade
(the facade’s metadata out-parameter), Media Types & Detection
(the Content-Type family of keys) and Setup & Installation
(dependencies).
All code on this page that prints output was compiled and run against Tika 4.1.0 (tika-core and
tika-parsers-standard-package as a pom dependency) on Java 21. The sample files are small stand-ins written for this
page: a one-heading manuscript.docx with a title and an author, a one-page catalogue.pdf (785 bytes), the same PDF
renamed to renamed-invoice.png, a hand-written publisher-email.eml with a PDF attachment and a short reviews-es.html.
The key names were additionally read from the tika-core 4.1.0, 3.3.2 and 1.28.5 source jars on Maven Central, and the
migration table was cross-checked against the machine-readable metadata-migration-3x-4x.json bundled in
tika-core 4.1.0. The section What was verified and what was not lists what could not be checked.
What metadata is, and what it is not
org.apache.tika.metadata.Metadata is a multi-valued map from a String key to one or more String values. It is the
parameter every Parser.parse call fills, the second result of Tika.parse, and the per-document record of a
RecursiveParserWrapper result. It carries three kinds of information:
| Kind | Examples | Who asserts it |
|---|---|---|
Document metadata |
|
The file. These are claims the document makes about itself. Treat them as untrusted input. |
Tika metadata |
|
Tika. These describe what the parse did. |
Caller hints |
|
The caller (or the transport, such as an HTTP header). They steer detection and parser choice. |
The distinction is the whole point of the 4.x key redesign: in 4.x every key that Tika itself computes lives under the
tk: prefix, and a file can no longer write into that namespace. A document that carries a custom property literally
named like a Tika key cannot overwrite the value Tika computed. The rest of this page builds on that idea.
Metadata is not the document text. In the Parser API the text goes to the ContentHandler; only the
RecursiveParserWrapper and Tika Pipes put the handler output into metadata, under the key tk:content. See
Introduction & Architecture for the four pillars.
The Metadata class
Metadata is small. Its useful surface is:
| Member | Behaviour |
|---|---|
|
All keys currently present, as a |
|
The first value of a key, or |
|
All values of a key, in insertion order, or an empty array. |
|
|
|
Replace all values of a key. A |
|
Append a value. Only allowed for bag and sequence properties; on a single-valued property a second |
|
Typed reads that parse the stored string and return |
|
String-keyed writes. In 4.x they throw |
|
The write route for key names that come from the document. See Document-derived names. |
|
Copy another |
|
Remove a key; count keys. |
|
A new |
|
A |
All values are stored as strings; typed access is convenience over parsing. Every string value is stored well-formed: a
lone UTF-16 surrogate is replaced by U+FFFD at write time (except for tk:content, which already went through a safe
handler), so no serializer downstream meets one.
Metadata is not thread safe. Create one per parse and do not share it between threads while a parse runs.
Reading metadata after a parse
The simplest useful program parses a file and prints every key. This is the dump used to produce the output tables later on this page.
import java.nio.file.Path;
import java.util.Arrays;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.sax.BodyContentHandler;
public class DumpMetadata {
public static void main(String[] args) throws Exception {
Path file = Path.of(args[0]);
Metadata metadata = new Metadata();
// A hint about the original name; detection can use it as a tie-breaker.
metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, file.getFileName().toString());
try (TikaInputStream tis = TikaInputStream.get(file, metadata)) {
new AutoDetectParser().parse(tis, new BodyContentHandler(-1), metadata, new ParseContext());
}
String[] names = metadata.names();
Arrays.sort(names);
for (String name : names) {
System.out.println(name + " = " + String.join(" | ", metadata.getValues(name)));
}
}
}
Run against manuscript.docx on Tika 4.1.0 (long values shortened here):
Content-Length = 1926
Content-Type = application/vnd.openxmlformats-officedocument.wordprocessingml.document
dc:creator = Ana Torres
dc:title = The Lighthouse Keeper
tk:content-type-magic-detected = application/vnd.openxmlformats-officedocument.wordprocessingml.document
tk:parsed-by = org.apache.tika.parser.DefaultParser | org.apache.tika.parser.microsoft.ooxml.OOXMLParser
tk:parsed-by-full-set = org.apache.tika.parser.DefaultParser | org.apache.tika.parser.microsoft.ooxml.OOXMLParser
tk:resource-name = manuscript.docx
zip:detector-zip-file-opened = true
Three observations follow from this output. The two document claims (dc:creator, dc:title) are bare Dublin Core
names. Everything Tika asserted is under tk:. And Content-Type is the one Tika-computed key that is not under tk::
it is the routing key every parser and consumer already depends on, so it keeps its name.
Source: Metadata, Javadoc 4.1.0 and
Java API, Apache Tika 4.1.x.
Multiple values
Many keys are bags: a document may have several creators, subjects or parsers. get returns the first value and hides
the others, which is a classic bug when indexing.
import java.util.Arrays;
import org.apache.tika.metadata.DublinCore;
import org.apache.tika.metadata.Metadata;
public class MultiValues {
public static void main(String[] args) {
Metadata metadata = new Metadata();
metadata.add(DublinCore.CREATOR, "Ana Torres");
metadata.add(DublinCore.CREATOR, "Marta Ruiz");
System.out.println(metadata.get("dc:creator")); // Ana Torres
System.out.println(Arrays.toString(metadata.getValues("dc:creator"))); // [Ana Torres, Marta Ruiz]
System.out.println(metadata.isMultiValued(DublinCore.CREATOR)); // true
}
}
On the Bookshelf manuscript.docx the tk:parsed-by key is multi-valued as well: it lists the DefaultParser
wrapper first and the parser that did the work second. Read it with getValues. Any index mapping for it must be a
multi-valued field.
Source: Metadata, Javadoc 4.1.0.
Typed Property keys
Since Tika 2.0 a key is not just a string: it is usually a org.apache.tika.metadata.Property constant that knows its
name, whether it is single- or multi-valued and what the value means. Property has two orthogonal characteristics.
| Characteristic | Values |
|---|---|
|
|
|
|
internal or external |
The XMP concept: an internal property is one the file format defines itself (for example PDF |
Public factories create properties in a parser module’s own namespace, for example
Property.internalText("myformat:title"), Property.internalTextBag(...), Property.internalDate(...),
Property.externalInteger(...) and Property.internalClosedChoice("tiff:Orientation", "1", ..., "8"). The
PropertyType of a key matters at write time:
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.PropertyTypeException;
import org.apache.tika.metadata.TikaCoreProperties;
public class PropertyTypes {
public static void main(String[] args) {
Metadata metadata = new Metadata();
// TikaCoreProperties.RESOURCE_NAME_KEY is a SIMPLE property: one value only.
metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, "ok.docx");
try {
metadata.add(TikaCoreProperties.RESOURCE_NAME_KEY, "again.docx");
} catch (PropertyTypeException e) {
System.out.println("second add on a SIMPLE property: " + e.getClass().getSimpleName());
}
// Typed access: an INTEGER property is written and read as int.
metadata.set(TikaCoreProperties.EMBEDDED_DEPTH, 2);
System.out.println("depth = " + metadata.getInt(TikaCoreProperties.EMBEDDED_DEPTH)); // 2
}
}
Both lines were run on Tika 4.1.0: the second add throws PropertyTypeException and the typed read returns 2. In
4.x some former String constants became SIMPLE properties instead of bags, so code that called add twice on them
now fails loudly rather than silently accumulating. Use set for a genuinely single value, or a bag-typed key if it can
repeat.
Dates are stored as ISO 8601 strings in UTC. metadata.set(TikaCoreProperties.CREATED, new Date(0)) stores
1970-01-01T00:00:00Z, and getDate parses it back. Parsers write dates through the Date and Calendar overloads, so
you can rely on this shape for Office, PDF and e-mail dates, but a value can still be absent or unparsable for a damaged
file.
Source: Property, Javadoc 4.1.0.
The standards Tika reuses
Tika did not invent a metadata vocabulary. It maps each format’s native fields onto a handful of existing standards and
exposes them as constants in tika-core (all in org.apache.tika.metadata). The first four rows are the ones to learn.
| Constants class | Key prefix | What it covers |
|---|---|---|
|
|
The cross-format keys: |
|
|
The Dublin Core elements: |
|
bare names |
|
|
|
Word-processor and spreadsheet statistics: |
|
|
Adobe XMP schemas. |
|
|
PDF structure and Info-dictionary keys ( |
|
|
Image keys: |
|
|
E-mail: |
|
|
PDF permission flags ( |
|
|
Format-specific families, kebab-cased in 4.x. |
The class list was taken from the org.apache.tika.metadata package of the 4.1.0 tika-core sources, and every key
spelled out above was read from those constants. The set is far larger than this table; the official
Adding a Metadata Key page notes that every key Tika
can emit is a Property constant or, for names minted at run time, a KeyPrefix, and that a generated registry (three
JSON files under tika-metadata-schema) lists all of them.
Metadata quality differs across formats
The same logical field is not available everywhere, and the value quality varies:
-
A
.docxor.odtcarries title and author as explicit properties, sodc:titleanddc:creatorare reliable when the author filled them in, and routinely empty when they did not. -
A PDF has two sources, the Info dictionary and an XMP packet. In 4.x the normalized keys
pdf:producer,dc:titleanddc:creatortake the XMP value when there is one and fall back to the Info dictionary otherwise; the individual sources stay visible aspdf:docinfo:*andxmp:pdf:*. -
An HTML page gives
<title>and<meta name=...>tags, which are written by whoever built the page and may be copied boilerplate. -
An image gives EXIF, XMP and IPTC fields from the camera or editor, which are often missing or scrubbed.
-
A plain text file has none of these. It has the
Content-Type,Content-Lengthand the encoding Tika detected.
Never use document metadata for a decision that needs trust (access control, ownership, billing). It is data the file author controls.
Source: Metadata Overview, Apache Tika wiki (written for the 2.x and 3.x generation, so key names there predate 4.x), DCMI Metadata Terms and Adobe XMP documentation.
The 4.x key design in one picture
A key falls into one of four families, and the family decides who may write it and how:
The next sections cover each family: first the tk: family and its migration, then the guard, then the KeyPrefix route.
The tk: namespace and the key changes in 4.x
What changed
Tika 4.0.0 consolidated every Tika-asserted metadata key under one prefix, tk:, normalized to kebab-case. It replaced
the 3.x X-TIKA: prefix and several scattered prefixes (tika:, tika_pg:, rendering:, signature:, imagereader:)
and a few bare keys (resourceName, hasSignature, embeddedResourceType). The official migration page calls this "a
clean break: there is no automatic remapping". Code that reads these keys by string must change; code that uses the
Java constants (TikaCoreProperties.TIKA_PARSED_BY and friends) keeps compiling, because the constants now resolve to
the tk: names.
|
The Tika 4.1.x FAQ says |
Other prefix changes in the same release:
| Category | Change in 4.0.0 |
|---|---|
HTML custom metadata |
Prefixed with |
Unrecognized image metadata |
Prefixed with |
Curated MAPI metadata |
Moved from |
QuattroPro metadata |
Moved from |
Format-namespace casing |
Names Tika coined are kebab-cased ( |
The Content-Type key itself did not change, but its Java constant did: Metadata no longer implements the
constant-holding interfaces (HttpHeaders, Message, TIFF and others), so Metadata.CONTENT_TYPE is gone and the code
becomes metadata.get(HttpHeaders.CONTENT_TYPE), which returns a Property-backed key. The compiler message
cannot find symbol: variable CONTENT_TYPE is the symptom.
The 1.x, 3.x and 4.x key-mapping table
The table maps the keys most Bookshelf code touches. 1.x is the generation documented by Tika in Action (Tika 1.0 in 2011; the key names below were checked in the 1.28.5 sources, the last 1.x release). 3.x was checked in 3.3.2. 4.x was checked in 4.1.0 and, for the highlighted rows, by running a parse. A dash means there was no such key in that generation, and "unverified" means the row could not be confirmed from source.
| Meaning | 1.x (book era) | 3.x | 4.x | Notes |
|---|---|---|---|---|
Media type |
|
|
|
Key unchanged. Java constant moved to |
Resource name (file name hint) |
|
|
|
Run on 4.1.0: |
Parsers that ran |
|
|
|
Multi-valued. The 1.28.5 source carries a comment about renaming it to the |
Full set of parsers |
not present in 1.28.5 |
|
|
Multi-valued. |
Extracted text (recursive result) |
|
|
|
Present in 1.28.5 in |
Embedded document path |
|
|
|
See Embedded Documents & Recursive Parsing for the whole containment model. |
Embedded depth |
|
|
|
Integer property. |
Embedded relationship id |
|
|
|
|
Embedded resource type |
|
|
|
Closed choice: |
Embedded stream exception |
|
|
|
All |
Container exception |
unverified |
|
|
|
Write limit reached |
unverified |
|
|
Boolean. Signals a truncated extraction. |
Truncated metadata warning |
unverified |
|
|
Set when the metadata limiter dropped data. |
Detected encoding |
- |
|
|
Run on 4.1.0 for |
Encoding detector |
- |
|
|
Run on 4.1.0: |
Detected language |
- |
|
|
Plus |
Digest |
unverified |
|
|
The algorithm is renamed to its JCA spelling in 4.x; encoding suffix kept ( |
Encrypted flag |
- |
|
|
|
Version count and number |
- |
|
|
Run on 4.1.0: |
Page number (paged text) |
- |
|
|
|
Signature fields |
- |
|
|
|
Title |
|
|
|
In 1.x |
Author |
|
|
|
In 1.x |
Last author |
|
|
|
|
Creation date |
|
|
|
Run on 4.1.0, the |
Page count |
|
|
|
Run on 4.1.0, the PDF reported |
E-mail sender |
|
|
|
Also |
E-mail raw header |
unverified |
|
|
Run on 4.1.0: |
PDF permission |
unverified |
|
|
Run on 4.1.0 for |
PDF XFA flag |
unverified |
|
|
Run on 4.1.0 for |
PDF version |
unverified |
|
|
Run on 4.1.0: |
HTML custom |
bare names such as |
bare names |
|
The sample |
Unrecognized image keys |
bare names |
bare names |
|
|
Office custom property |
|
|
|
See the unresolved note below the table. |
Notes on the table:
-
The "1.x" column lists what 1.28.5 source shows, not what Tika in Action printed in 2011, which targeted 1.0 and predates some of these constants. Treat the book’s
Metadata.AUTHORstyle listings as outdated idioms: those constants are deprecated in 1.x, and removed or moved in 3.x and 4.x. -
Rows marked from "the official table" come from the Tika 4.1.x Metadata Changes in Tika 4.x page and were not reproduced by a run.
-
Unresolved: for an Office custom (user-defined) property the 4.1.x migration page lists
meta:some-propertytooffice:some-property, butOffice.USER_DEFINED_METADATA_NAME_PREFIXin the 4.1.0tika-coresources iscustom:andmetadata.add(Office.USER_DEFINED, "Shelf", "A12")producedcustom:Shelfwhen run. The OOXML parser module may use its own prefix; this could not be settled without a document that has custom properties, so check your own files before relying on either spelling. -
The bundled
metadata-migration-3x-4x.json(253 rows in 4.1.0) is broader than the tables on the official page. For example it maps theMessage-*family, which the page tables do not list. It is the authoritative key-for-key list.
New in 4.x with no 3.x predecessor
These keys have nothing to migrate from. Useful ones for intake code:
| Key | Meaning |
|---|---|
|
Chunks of the extracted content (4.0), used by embedding and RAG pipelines. |
|
The handler type enum name (replaces the dropped 3.x |
|
The type detected from magic bytes, before hints. Run on 4.1.0: present on every sample. |
|
The charset used to decode text, and the detectors' decisions, for example |
|
How much content detection read and whether it was cut short. |
|
Whether the resource name’s extension was inferred rather than supplied. |
|
A depth limit stopped recursion. |
|
Tika released inference resources for the document. |
|
1-based pages a rendering engine could not draw. |
|
Integrity checks for ZIP containers, for example |
|
Token usage of the new VLM parsers. |
Dropped, not renamed
Some 3.x keys have no 4.x equivalent. The migration JSON marks them DROPPED, for example X-TIKA:content_handler,
pst:folderPath (the Outlook parser now folds the folder into tk:internal-path, a superset) and cp:subject. A script
that migrates an index must handle both the renamed and the dropped rows, plus the prefix families (NER_* to ner:*,
iworks:* to iwork:*, MDB_PROP:* to mdb-prop:*, Message:Raw-Header:* to message:raw-header:*, digests, and the
open-vocabulary GDAL, HDF, NetCDF and ISO 19115 families).
Source: Metadata Changes in Tika 4.x, Apache
Tika 4.1.x and
metadata-migration-3x-4x.json on GitHub
(the file bundled in tika-core; the path was read from the 4.1.0 sources jar, the GitHub URL is the project’s
repository layout and was not fetched).
Restoring 3.x key names with LegacyKeyMigrationFilter
If downstream consumers (an index schema, a dashboard) cannot move to tk: names yet, Tika does not ask you to fork it.
The opt-in LegacyKeyMigrationFilter metadata filter rewrites keys at the emit edge using the same migration table. It
works in either direction and is configured in the parse-context section of tika-config.json:
{
"parse-context": {
"legacy-key-migration-filter": {}
}
}
The default direction is V4_TO_V3: Tika’s 4.x output is rewritten into 3.x spellings. For input that is 3.x shaped and
must be read with 4.x names, set direction:
{
"parse-context": {
"legacy-key-migration-filter": { "direction": "V3_TO_V4" }
}
}
An optional table overrides the bundled table with a custom one in the same row shape ({"v3": "...", "v4": "..."},
with "v4": "DROPPED" for a key that has no successor). The filter is also usable programmatically, as in this run
against the metadata of catalogue.pdf:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.filter.LegacyKeyMigrationFilter;
public class LegacyKeys {
public static void main(String[] args) throws Exception {
// 3.x shaped input, mapped forward to 4.x names
Metadata threeX = new Metadata();
threeX.setTrusted("X-TIKA:Parsed-By", "org.apache.tika.parser.DefaultParser");
threeX.setTrusted("resourceName", "catalogue.pdf");
threeX.setTrusted("pdf:hasXFA", "false");
LegacyKeyMigrationFilter.Config config = new LegacyKeyMigrationFilter.Config();
config.direction = LegacyKeyMigrationFilter.Direction.V3_TO_V4;
List<Metadata> list = new ArrayList<>(List.of(threeX));
new LegacyKeyMigrationFilter(config).filter(list);
String[] names = threeX.names();
Arrays.sort(names);
System.out.println(Arrays.toString(names));
}
}
Output on Tika 4.1.0:
[pdf:has-xfa, tk:parsed-by, tk:resource-name]
The reverse direction (the default) turned the 4.x metadata of catalogue.pdf into keys such as X-TIKA:Parsed-By,
X-TIKA:Parsed-By-Full-Set, X-TIKA:digest:SHA256, X-TIKA:versionCount and access_permission:can_print. The filter
changes the metadata in place.
There is one limit worth knowing: families whose 3.x keys were completely bare (HDF, NetCDF, residual GDAL keys, ISA-Tab, QuickTime, audio SPI keys) are restored only when going 4.x to 3.x, because on ingest a bare name does not identify its family. The filter passes them through when migrating forward.
The reserved-key write guard
In 3.x a call such as metadata.set("X-TIKA:Parsed-By", value) simply succeeded, so a custom property in a hostile
document, written by a parser through a string key, could replace a value Tika had computed. In 4.0 the string-keyed
writes set(String, String) and add(String, String) throw IllegalArgumentException for any reserved name: a
tk: key, and, during the 4.x line, the legacy X-TIKA: prefix.
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;
public class ReservedKeys {
public static void main(String[] args) {
Metadata metadata = new Metadata();
try {
metadata.set("tk:resource-name", "evil.docx");
} catch (IllegalArgumentException e) {
System.out.println("rejected: " + e.getMessage());
}
// The supported route is the Property constant.
metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, "ok.docx");
System.out.println(metadata.get("tk:resource-name")); // ok.docx
}
}
On Tika 4.1.0 the first call fails with the message "Writing reserved key 'tk:resource-name' via the String API is not
allowed", which names the three legitimate alternatives: the Property constant, add(KeyPrefix, ...) for
document-derived names, and putAll or setTrusted for intentional reconstruction. The same message appears for
X-TIKA:Parsed-By. A null value (a removal) is always allowed.
| Need | Do this |
|---|---|
Set a key Tika owns |
Use its |
Copy metadata between objects |
|
Rebuild metadata from JSON or a database (metadata filters, deserializers) |
|
Write names that come from the document |
|
Three further consequences matter in production:
-
The guard throws an
IllegalArgumentException, aRuntimeException, not aTikaException. A harness that narrowly catchesTikaExceptionaround a parse will see an unmigrated parser hitting a crafted file as a new uncaught failure. -
Property's public factories reject a reserved name at construction. Because those calls usually initializestatic finalconstants, the failure surfaces asExceptionInInitializerError(thenNoClassDefFoundErroron later use) far from the declaration; read the cause for the reserved name. -
/pipesand/asyncrequests may still carry their owntk:-named user metadata, and it lands in that request’s output; this is deliberate and bounded to the requester’s own request.
Document-derived names and KeyPrefix
When key names come from the document (HTML <meta name=...> tags, custom Office properties, CSV headers, e-mail
headers) they cannot be constants. A parser declares a KeyPrefix once, as a static final field, and writes through
Metadata#add(KeyPrefix, String, String):
import org.apache.tika.metadata.KeyPrefix;
import org.apache.tika.metadata.Metadata;
public class ShelfHeaders {
// Declared once per parser module: never per parse, never from document text.
static final KeyPrefix SHELF = KeyPrefix.file("shelf:", "the intake sheet's own header fields");
static void record(Metadata metadata, String nameFromDocument, String value) {
metadata.add(SHELF, nameFromDocument, value);
}
}
The route has these guarantees: it is append-only (a repeated name accumulates values), it never throws on hostile input
(a blank name, an over-length name, or a flood of distinct names is skipped with a warning), and source-typed dates have
an Instant overload that stores canonical ISO 8601. Use KeyPrefix.file(...) for names read from the document and
KeyPrefix.tool(...) for names coined by an external tool. The same route is what produced these keys in runs on Tika
4.1.0: message:raw-header:Message-ID and message:raw-header:MIME-Version for the e-mail, and custom:Shelf when the
Office.USER_DEFINED prefix was used directly.
A parser module author should know that keys with a known, bounded vocabulary belong in curated Property constants,
and that adding a key to TikaCoreProperties requires the build’s registry regeneration (tika-metadata-schema/regen.sh).
That is contributor-level detail; the Extending Tika page covers
custom parsers.
Metadata filters
A metadata filter rewrites metadata after parsing and before it is emitted or serialized: the last chance to drop a
field, rename it, normalize it or add a derived one. Filters run in tika-server, Tika Pipes and (for its -J output
only, in 4.1.0) tika-app, on the whole list of metadata objects (the container document first, then each embedded document).
metadata-filters is a top-level array in tika-config.json. Filters run in the listed order, each seeing the previous
output. An element is a filter name (default settings) or an object keyed by the name with its configuration. There is
no default filter and no implicit filtering: omit the section and the metadata is emitted unchanged.
| Name | What it does | Options (default) |
|---|---|---|
|
Keeps only the named fields. |
|
|
Drops the named fields. |
|
|
Renames fields; with |
|
|
Removes a document’s whole |
|
|
Empties (but keeps) the metadata of an embedded document whose |
|
|
Rewrites DATE-typed fields lacking a timezone to ISO 8601 UTC. |
|
|
Regex over the first value of |
|
|
Writes |
|
|
Rewrites |
|
|
Does nothing. |
none |
Optional modules add filters when on the classpath: the language detectors (charsoup-metadata-filter,
optimaize-metadata-filter, open-nlp-metadata-filter), tika-eval-metadata-filter for extraction-quality scores, and
the deprecated embedding filters of tika-inference (deprecated in 4.1.0, removed in 4.2.0 according to the 4.1.x
documentation).
In 4.1.0, tika-eval-metadata-filter cannot be named in tika-config.json: the load fails with "Component … is not
registered" because the tika-eval-core jar does not register it. Add it from Java instead, as
tika-eval & Extraction Quality shows.
Emit only the fields your index needs
The Bookshelf search schema only needs a handful of fields. An allow-list keeps the index small and avoids storing document-controlled keys nobody queries:
{
"metadata-filters": [
{
"include-field-metadata-filter": {
"include": ["tk:content", "Content-Type", "dc:title", "dc:creator"]
}
}
]
}
The same filter was exercised in code on Tika 4.1.0: a metadata object holding tk:resource-name, Content-Type and
dc:title, filtered with an include set of Content-Type and dc:title, kept exactly those two.
Rename fields for a downstream schema
excludeUnmapped defaults to true, so this keeps only the four mapped fields under their new names:
{
"metadata-filters": [
{
"field-name-mapping-filter": {
"excludeUnmapped": true,
"mappings": {
"tk:content": "content",
"Content-Type": "mime",
"dc:title": "title",
"dc:creator": "author"
}
}
}
]
}
Strip media-type parameters, and drop embedded images
Content-Type often arrives as text/plain; charset=UTF-8. To index only the base type, copy the part before the
semicolon back onto the same field:
{
"metadata-filters": [
{
"capture-group-metadata-filter": {
"sourceField": "Content-Type",
"targetField": "Content-Type",
"regex": "\\A([^;]+)"
}
}
]
}
To keep inline images out of an .docx result without losing the text, blank their metadata, or remove those documents
from the list altogether by media type:
{
"metadata-filters": [
{ "clear-by-attachment-type-metadata-filter": { "types": ["INLINE"] } },
{ "remove-by-mime-metadata-filter": { "mimes": ["image/jpeg", "image/png"] } }
]
}
Ordering and where filters apply
Composition is literal: filter n sees the output of filter n-1. An allow-list therefore runs last, or it removes the
fields a later filter wanted (an include-field-metadata-filter before geo-point-metadata-filter drops the latitude and
longitude unless they are included). A rename also runs last, or later filters must use the new names.
Filters apply in tika-app before writing -J output (in a 4.1.0 run, -m and -j ignored the chain, as
Configuration shows), in tika-server's /tika, /rmeta, /meta and /unpack inside the
forked JVM before the response is written, and in Tika Pipes at the emit edge. A parse-context may carry a per-request
metadata-filters list that replaces the configured one for that request. In the CONTENT_ONLY Pipes parse mode,
Tika applies an include-field-metadata-filter for tk:content and tk:exception:container-exception by default when
you configured none, and the top-level chain is not consulted; if you supply a filter in that mode you are responsible
for keeping tk:content.
The metadata limiter
A hostile or just unlucky file can carry a huge amount of metadata: a million custom properties, a 50 MB value. The
metadata limiter bounds the size of each parse’s metadata at write time, before memory is spent. It is the
StandardMetadataLimiterFactory, configured under parse-context as standard-metadata-limiter-factory:
{
"parse-context": {
"standard-metadata-limiter-factory": {
"maxTotalBytes": 1048576,
"maxFieldSize": 102400,
"maxKeySize": 1024,
"maxValuesPerField": 100
}
}
}
The defaults, read from the 4.1.0 source, are:
| Setting | Default | Meaning |
|---|---|---|
|
1,024 |
Maximum key size in UTF-16 bytes; longer keys are truncated. |
|
102,400 (100 KiB) |
Maximum size of one value; longer values are truncated. |
|
10,485,760 (10 MiB) |
Estimated total size of all keys and values in the object. |
|
10 |
Maximum number of values for a multi-valued key. |
|
empty |
Allow-list or deny-list of key names. Empty include means all fields. |
|
|
Whether empty values are stored. |
Some keys bypass the limiter so that Tika’s own machinery keeps working: Content-Type, Content-Length,
Content-Encoding, Content-Disposition, tk:content, tk:resource-name, the tk:content-type-* hints and two PDF
access-permission keys always get through, with a minimum field size of 300 bytes. When data is dropped, Tika sets
tk:warn:truncated-metadata to true; check it before trusting that a record is complete.
To apply the limiter in code, put the factory on the ParseContext and create the Metadata with
Metadata.newInstance(context):
import java.nio.file.Path;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.writelimiter.MetadataWriteLimiterFactory;
import org.apache.tika.metadata.writelimiter.StandardMetadataLimiterFactory;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.sax.BodyContentHandler;
public class LimitedMetadata {
public static void main(String[] args) throws Exception {
StandardMetadataLimiterFactory limiter = new StandardMetadataLimiterFactory();
limiter.setMaxValuesPerField(2);
limiter.setMaxFieldSize(10 * 1024);
ParseContext context = new ParseContext();
context.set(MetadataWriteLimiterFactory.class, limiter);
Metadata metadata = Metadata.newInstance(context);
try (TikaInputStream tis = TikaInputStream.get(Path.of("catalogue.pdf"))) {
new AutoDetectParser().parse(tis, new BodyContentHandler(-1), metadata, context);
}
System.out.println(metadata.get("tk:warn:truncated-metadata")); // null when nothing was dropped
}
}
This compiled and ran on Tika 4.1.0 against the sample PDF. With a maximum of two values the two-valued tk:parsed-by
fit, so nothing was dropped and the warning key was absent. Parsers that create metadata for embedded documents should
use Metadata.newInstance(context) rather than new Metadata(), so the same limits apply to children.
Digests
Tika can hash documents while parsing, for deduplication, integrity checks and forensics. Digests land in metadata under
tk:digest:<ALGORITHM> (hex encoding, the default) or tk:digest:<ALGORITHM>:<ENCODING> for other encodings, for
example tk:digest:SHA-256:BASE32. The key carries the algorithm’s JCA name (SHA-256, SHA3-512), while the
configuration names it by the enum spelling (SHA256, SHA3_512). Two factories are configured in parse-context:
| Factory | Config key | Algorithms |
|---|---|---|
|
|
MD2, MD5, SHA1, SHA256, SHA384, SHA512 (Apache Commons Codec). |
|
|
All of the above plus SHA3_256, SHA3_384, SHA3_512 (BouncyCastle provider, extra dependency). |
Encodings are HEX (default, lowercase), BASE32 and BASE64, both RFC 4648. In 3.x the key was X-TIKA:digest:SHA256,
so a digest-keyed index needs the LegacyKeyMigrationFilter or a migration.
{
"parse-context": {
"commons-digester-factory": {
"digests": [
{ "algorithm": "MD5", "encoding": "BASE32" },
{ "algorithm": "SHA256" }
],
"skipContainerDocumentDigest": false
}
}
}
skipContainerDocumentDigest set to true digests only the embedded documents of a container (say, the PDFs inside a
ZIP) and not the container. Programmatically:
import java.nio.file.Path;
import java.util.Arrays;
import org.apache.tika.digest.DigestDef;
import org.apache.tika.digest.DigesterFactory;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.digestutils.CommonsDigesterFactory;
import org.apache.tika.sax.BodyContentHandler;
public class DigestsInMetadata {
public static void main(String[] args) throws Exception {
CommonsDigesterFactory factory = new CommonsDigesterFactory();
factory.setDigests(Arrays.asList(
new DigestDef(DigestDef.Algorithm.SHA256),
new DigestDef(DigestDef.Algorithm.MD5, DigestDef.Encoding.BASE32)));
ParseContext context = new ParseContext();
context.set(DigesterFactory.class, factory);
Metadata metadata = new Metadata();
try (TikaInputStream tis = TikaInputStream.get(Path.of("catalogue.pdf"))) {
new AutoDetectParser().parse(tis, new BodyContentHandler(-1), metadata, context);
}
System.out.println("tk:digest:SHA-256 = " + metadata.get("tk:digest:SHA-256"));
System.out.println("tk:digest:MD5:BASE32 = " + metadata.get("tk:digest:MD5:BASE32"));
}
}
Run on Tika 4.1.0 against the 785-byte catalogue.pdf, the keys were tk:digest:SHA-256 (a 64-character hex value) and
tk:digest:MD5:BASE32 (a base32 value padded with =). The digest needs the bytes: Tika reads the stream, spooling it
to a temporary file when the digester requires it, so enabling digests adds I/O. On the command line, tika-app's
--digest=<algorithm> covers the Commons algorithms only; for SHA3 use a JSON configuration with the BouncyCastle factory.
Keys to read for each Bookshelf file
The table lists the keys worth storing for each file of the intake scenario. Rows marked run were produced by a parse on
Tika 4.1.0 of the stand-in sample; rows marked source come from constants in the 4.1.0 tika-core source and were not
observed with a real file (the book-cover.jpg, scanned-loan-form.pdf and novel.epub samples were not run for this
page).
| File | Keys worth keeping | Basis |
|---|---|---|
|
|
run (first five), source (the rest) |
|
|
run |
|
|
source and run (the |
|
|
source |
|
|
run |
|
|
source (Dublin Core constants; EPUB keys not run) |
|
|
run |
|
|
source (txt files not run) |
|
|
run |
The last row is the point of the example on the
Media Types & Detection page. The same PDF, called
renamed-invoice.png, produced the
Content-Type application/pdf, kept tk:resource-name = renamed-invoice.png and added
tk:content-type-magic-detected = application/pdf. An intake service should compare what the file name claims with
Content-Type and reject or quarantine a mismatch. See
Media Types & Detection for validating uploads by content.
Pitfalls
-
Reading with
getwhen the key is a bag.dc:creator,dc:subjectandtk:parsed-bycan hold several values; usegetValues. -
Keeping the 3.x names. Code that does
metadata.get("X-TIKA:Parsed-By")ormetadata.get("resourceName")returnsnullon 4.x without an error. Grep your code and index schemas forX-TIKA,resourceName,access_permission,Message-From,pdf:hasXFA, or enableLegacyKeyMigrationFilter. -
Trusting the file’s claims.
dc:creatoris whatever the author typed. Do not use it for authorization. -
Writing
tk:keys by string. It throws. UseProperty,putAllor, in trusted code,setTrusted. -
Narrow
catch (TikaException e). The guard andPropertyTypeExceptionare `IllegalArgumentException`s. -
Assuming
Content-Typeis a bare media type. It can carry parameters (text/html; charset=UTF-8); useMediaType.parseor the capture-group filter above. -
Unbounded metadata at scale. Without a limiter, a crafted file can carry megabytes of properties. Configure
standard-metadata-limiter-factoryfor untrusted input and checktk:warn:truncated-metadata. -
Copy loops. Use
putAll. -
Assuming
tk:resource-nameis the upload name. Some parsers overwrite it (the e-mail parser set it from the subject). Keep your own copy of the original file name.
What was verified and what was not
Verified for this page:
-
The
tk:key names fortk:parsed-by,tk:parsed-by-full-set,tk:resource-name,tk:content-type-magic-detected,tk:detected-encoding,tk:encoding-detector,tk:encoding-detection-trace,tk:version-count, the digest keys and the reserved-key guard behaviour, by running parses on Tika 4.1.0. -
The names of the
tk:constants inTikaCoreProperties, theMetadatamethod set, the limiter defaults and the always-allowed field set, by reading the 4.1.0tika-coresources. -
The 1.x and 3.x key names in the mapping table for the rows described as checked, by reading the 1.28.5 and 3.3.2
tika-coresource jars. -
The existence and behaviour of
LegacyKeyMigrationFilterin both directions, by running it.
Not verified:
-
The tika-server JSON output was not inspected on this page. The
tk:names in tika-app output (includingtk:contentin-J) are confirmed by runs on the Embedded Documents & Recursive Parsing and Configuration pages. -
The 3.x
embeddedResourceType, the 1.x forms of the exception and digest keys (marked "unverified" in the table), and all "per the official table" rows, which come from the Tika 4.1.x migration page only. -
The Office custom-property prefix (
custom:in source versusoffice:in the migration page), as explained above. -
The keys produced by real JPEG, EPUB, scanned PDF and encoded text samples (marked source in the table).
-
The exact Tika in Action listings: the 1.x column reflects 1.28.5, not the 1.0 sources the book was written for.
-
The GitHub URL of the migration JSON in the Source line, which follows the repository layout and was not fetched.
-
The Tika 4.1.x documentation says its index page was generated with AI assistance and needs human review, so the pages cited here were cross-checked against sources and runs wherever the table above says so.