Tika App (Command Line & GUI)

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 for the integration pages, and the Docker image apache/tika:4.1.0 for the service pages. tika-app is the Tika you can use without writing code: one command that parses a file, a URL or standard input and prints the text, the metadata, the media type or the language, a small desktop window for drag-and-drop, and, since 4.0, a front end to Tika Pipes that processes whole directories in forked JVMs. This page covers the three modes, every option of the 4.1.0 release, the options that were removed, and the Bookshelf intake desk using the tool to look at the files it receives.

The tika-app CLI is still in flux for 4.x

The official 4.1.x CLI page opens with the warning that the command line interface "is still in flux for 4.x". Options and behavior may change between 4.x releases. The same page says the authoritative option list is the --help output of the version you run, and that --help wins if the two disagree. Everything on this page was therefore checked against the --help output and the source of 4.1.0. Run --help on your own version before you copy a command into a script, and pin the tika-app version in any script you keep.

Related pages: Setup & Installation (the zip distributions and Java requirement), Content Handlers & Output Formats (what each output format contains), Metadata (the tk: keys you will see in the output), Embedded Documents & Recursive Parsing (-J and unpacking), Configuration (tika-config.json) and Tika Pipes (the engine behind pipes mode).

How this page was verified: the official tika-app-4.1.0.zip was downloaded from the Apache archive (https://archive.apache.org/dist/tika/4.1.0/), its SHA-512 checksum was compared with the published .sha512 file, and it was unzipped into a working directory. Every command on this page that shows output was run from that distribution with java -jar tika-app-4.1.0.jar on Java 25 (Oracle JDK 25.0.2) on macOS, and the output is copied from the terminal, shortened only where marked. The option semantics were read from the 4.1.0 source of TikaCLI, AsyncHelper and the tika-async-cli module (TikaAsyncCLI, PluginsWriter) at the 4.1.0 Git tag. The sample files are small stand-ins written for this section: a one-heading manuscript.docx (title The Lighthouse Keeper, author Ana Torres), a one-page catalogue.pdf (785 bytes), the same PDF renamed to renamed-invoice.png, a password-protected members-only.pdf, a windows-1252 legacy-catalogue.txt, a tiny novel.epub, a Spanish reviews-es.html, a French reseñas-fr.txt and publisher-email.eml, an e-mail that carries samples.zip. The GUI was not opened for this page (see The GUI).

What tika-app is

tika-app is a Maven module and a release artifact. Its main class, org.apache.tika.cli.TikaCLI, wraps the same components the Java pages of this section use: a TikaLoader built from a configuration file, an AutoDetectParser, a ParseContext and a content handler chosen by an option. Since Tika 4.0 it also wraps PipesForkParser (fork mode) and the Tika Pipes AsyncProcessor (pipes mode). In short, it is a convenient way to see what Tika will do with a file before you write code, to script one-off extractions and, in pipes mode, to run a batch.

It runs in three modes. The official page names them standard, GUI and Tika Pipes. Fork mode is an option of the standard mode.

Mode What it does How you get it

Standard

Parses one or more files, URLs or standard input in the CLI JVM and writes the result to standard output.

A file, a URL, - or nothing on standard input, with output options.

Standard + fork

Same output, but the parse runs in a forked JVM through PipesForkParser, so a crash, an OutOfMemoryError or a hang cannot take the CLI down.

Add -f or --fork.

GUI

A Swing window. Drop a file on it and look at its metadata, text, XHTML and recursive JSON.

-g or --gui, or no arguments and nothing on standard input.

Tika Pipes

Processes every file under a directory (or a file list, or any configured pipes iterator) in forked JVMs and writes one output file per input.

Two directories, -i / -o, -z / -Z, --fileList, -a, or a single .json pipes configuration.

The mode is picked from the arguments before any option is applied. The following flow is how TikaCLI.main in 4.1.0 decides (help first, then the pipes test, then the standard per-argument loop).

flowchart TD A[java -jar tika-app.jar args] --> B{-? or --help anywhere?} B -- yes --> H[print usage and exit] B -- no --> C{pipes trigger?} C -- "two dirs, a single .json, -i, -o, -z, -Z, --extract, --fileList, -a" --> P[Tika Pipes mode: banner on stderr, AsyncProcessor, forked JVMs] C -- no --> D{any arguments?} D -- none --> E{data on stdin within 0.1 s?} E -- yes --> S[standard mode on stdin] E -- no --> G[GUI] D -- yes --> L[standard mode: options and files processed left to right] L --> F{-f or --fork set?} F -- yes --> K[parse in a forked JVM through PipesForkParser] F -- no --> I[parse in the CLI JVM]

Installing the zip distribution

Tika 4.x ships tika-app as a zip, not as the single fat jar of the 1.x to 3.x lines. The jar inside the zip is a thin launcher: its manifest has a Class-Path that lists the jars in the adjacent lib/ directory, and pipes mode loads its fetcher and emitter plugins from the adjacent plugins/ directory. The zip has no top-level directory, so unzip it into one.

curl -O https://archive.apache.org/dist/tika/4.1.0/tika-app-4.1.0.zip
curl -O https://archive.apache.org/dist/tika/4.1.0/tika-app-4.1.0.zip.sha512
shasum -a 512 tika-app-4.1.0.zip    # compare with the content of the .sha512 file
cat tika-app-4.1.0.zip.sha512

unzip -q -d tika-app-4.1.0 tika-app-4.1.0.zip
ls tika-app-4.1.0

Output (the two checksum lines were identical and are shortened here):

e8ffd4b85f08b2c9...25487c8745d  tika-app-4.1.0.zip
e8ffd4b85f08b2c9...25487c8745d
LICENSE  NOTICE  lib  plugins  tika-app-4.1.0.jar

What is inside (4.1.0):

Entry Content

tika-app-4.1.0.jar

TikaCLI, the GUI, the default single-file configuration and a log4j2.xml. Its manifest declares Java-Version: 17 and a Class-Path of lib/... entries.

lib/

110 jars, about 57 MB: tika-core, every module of the standard parser package, tika-langdetect-charsoup, tika-vlm, tika-inference, tika-async-cli, the pipes core, POI, PDFBox, Bouncy Castle and the rest.

plugins/

The file-system pipes plugin (tika-pipes-file-system-4.1.0.zip and its unpacked directory), which pipes mode uses to read the input directory and write the output directory.

LICENSE, NOTICE

The Apache license and notices.

The Class-Path entries are resolved relative to the jar, not to the current directory, so java -jar /opt/tika-app-4.1.0/tika-app-4.1.0.jar works from any working directory. All the examples on this page were run that way from a directory that held the sample files. The jar on its own does not work. Copied out of the distribution it fails before reading any argument:

cp tika-app-4.1.0/tika-app-4.1.0.jar /some/other/dir/
java -jar /some/other/dir/tika-app-4.1.0.jar --version

Output:

Error: Unable to initialize main class org.apache.tika.cli.TikaCLI
Caused by: java.lang.NoClassDefFoundError: org/apache/tika/mime/MimeTypeException

This is the NoClassDefFoundError the 4.x FAQ and the CLI page describe. If pipes mode is started from an incomplete distribution, TikaCLI catches the error and prints an explanation (download the zip, run the jar from inside it); the standard mode fails in the JVM launcher, as above. The tika-app artifact on Maven Central is the same thin jar plus a POM with the dependencies, which is how the earlier pages of this section ran TikaCLI on a Maven-built classpath.

A shell alias keeps the commands short. The rest of the page writes tika for it:

alias tika='java -jar /opt/tika-app-4.1.0/tika-app-4.1.0.jar'
tika --version

Output:

Apache Tika 4.1.0

The JVM options go before -jar, as for any Java program: java -Xmx2g -jar tika-app-4.1.0.jar ... sets the heap of the CLI JVM in standard mode. The forked JVMs of fork mode and pipes mode get their own options through --fork-jvm-args and --Xmx, described below.

Logging and the convenience-defaults banner

Log lines go to standard error through Log4j 2, so standard output stays clean for redirection and pipes. The bundled log4j2.xml logs at INFO, and every standard-mode run without --config logs this notice first:

INFO  [main] 22:53:16,719 org.apache.tika.cli.TikaCLI As a convenience, TikaCLI has turned on several non-default features
as specified in tika-app/src/main/resources/tika-config-default-single-file.json.
See: TIKA-2374, TIKA-4017, TIKA-4354 and TIKA-4472).
This is not the default behavior in Tika generally or in tika-server.

The notice is accurate and worth reading. Without --config, tika-app loads a bundled configuration that turns on features the library and tika-server leave off. The file in the 4.1.0 jar is:

{
  "parsers": [
    { "default-parser": {} },
    { "pdf-parser": {
        "extractActions": true,
        "extractInlineImages": true,
        "accessCheckMode": "ALLOW_EXTRACTION_FOR_ACCESSIBILITY",
        "extractIncrementalUpdateInfo": true,
        "parseIncrementalUpdates": true } },
    { "ooxml-parser": {
        "includeDeletedContent": true,
        "includeMoveFromContent": true,
        "extractMacros": true } },
    { "office-parser": { "extractMacros": true } }
  ]
}

So tika-app shows PDF actions, inline images and incremental updates, and Word’s deleted and moved text and macros, by default. That is useful when you are inspecting a file, and it is the reason the same file can give a little more output in tika-app than in your own AutoDetectParser code. Pass --config= with your production configuration when you want to see exactly what your service will see. The pipes mode template has the same parser settings (see Defaults of pipes mode).

To keep only warnings and errors, point Log4j at your own file:

<?xml version="1.0" encoding="UTF-8"?>
<!-- quiet-log4j2.xml: only WARN and above, on standard error -->
<Configuration status="WARN">
  <Appenders>
    <Console name="stderr" target="SYSTEM_ERR">
      <PatternLayout pattern="%-5p %c{1} %m%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="warn"><AppenderRef ref="stderr"/></Root>
  </Loggers>
</Configuration>
java -Dlog4j.configurationFile=quiet-log4j2.xml -jar tika-app-4.1.0.jar -t catalogue.pdf

Output (standard error was empty):

Bookshelf spring catalogue
Forty titles from independent publishers.

-v / --verbose does the opposite and raises the root logger to DEBUG (a -j -v run on manuscript.docx printed 43 DEBUG lines on standard error).

Source: Apache Tika 4.1.x, Tika-App Integration Testing (its known issue "INFO message on every default-config command") and Apache Log4j 2, Configuration.

The real --help output of 4.1.0

This is the complete, unedited --help output of the 4.1.0 distribution (it goes to standard output and the command exits with status 0). It is the reference for the rest of the page.

java -jar tika-app-4.1.0.jar --help

Output:

usage: java -jar tika-app.jar [option...] [file...]

Options:
    -?  or --help          Print this usage message
    -v  or --verbose       Print debug level messages
    -V  or --version       Print the Apache Tika version number

    -g  or --gui           Start the Apache Tika GUI

    --config=<tika-config.json>
        TikaConfig file (JSON as of Tika 4.x). Must be specified before -g or -f !
    --convert-config-xml-to-json=<input.xml>
        Convert a legacy 3.x XML config to 4.x JSON format (parsers section only),
        writing the JSON to stdout. Redirect to save, e.g.:
        --convert-config-xml-to-json=tika-config.xml > tika-config.json

    -x  or --xml           Output XHTML content
    -h  or --html          Output HTML content
    -t  or --text          Output plain text content (body)
    --md                   Output Markdown content (body) (default)
    -T  or --text-main     Output plain text content (main content only via boilerpipe handler)
    -A  or --text-all      Output all text content
    -m  or --metadata      Output only metadata
    -j  or --json          Output metadata in JSON
    -y  or --xmp           Output metadata in XMP
    -J  or --jsonRecursive Output metadata and content from all
                           embedded files (choose content type
                           with -x, -h, -t or -m; default is --md)
    -a  or --async         Run Tika in async mode: -a -c <tika-config.json> [-p <pluginsDir>]
    -l  or --language      Output only language
    -d  or --detect        Detect document type
           --digest=X      Include digest X (md2, md5, sha1,
                               sha256, sha384, sha512)
    --encoding=X           Use output encoding X
    --password=X           Use document password X
    -z  or --extract       Extract all attachements into current directory
    --extract-dir=<dir>    Specify target directory for -z
    --maxEmbeddedDepth=X   Maximum depth for embedded document extraction
    --maxEmbeddedCount=X   Maximum number of embedded documents to extract
    -r  or --pretty-print  For JSON, XML and XHTML outputs, adds newlines and
                           whitespace, for better readability

Fork Mode (process isolation):
    -f  or --fork          Run parsing in a forked JVM process for isolation
                           Protects against parser crashes, OOM, and timeouts
    --task-timeout=<ms>    Total per-file budget in milliseconds
                           (default: 3600000)
    --progress-timeout=<ms> Stall detector: max time with no progress
                           (default: 120000)
    --fork-jvm-args=<args> JVM args for forked process (comma-separated)
                           e.g., --fork-jvm-args=-Xmx512m,-Dsome.prop=value
    --fork-plugins-dir=<dir> Directory containing plugin zips

    --list-parsers
         List the available document parsers
    --list-parser-names
         List parsers as tab-separated class-name<TAB>friendly-name
         (friendly names are the kebab-case keys used in JSON config)
    --list-parser-details
         List the available document parsers and their supported mime types
    --list-parser-details-apt
         List the available document parsers and their supported mime types in apt format.
    --list-parser-details-adoc
         Same, in AsciiDoc; regenerates docs/modules/ROOT/partials/supported-formats.adoc
    --list-detectors
         List the available document detectors
    --list-detector-names
         List detectors as tab-separated class-name<TAB>friendly-name
    --list-met-models
         List the available metadata models, and their supported keys
    --list-supported-types
         List all known media types and related information


    --compare-file-magic=<dir>
         Compares Tika's known media types to the File(1) tool's magic directory
Description:
    Apache Tika will parse the file(s) specified on the
    command line and output the extracted text content
    or metadata to standard output.

    Instead of a file name you can also specify the URL
    of a document to be parsed.

    If no file name or URL is specified (or the special
    name "-" is used), then the standard input stream
    is parsed. If no arguments were given and no input
    data is available, the GUI is started instead.

- GUI mode

    Use the "--gui" (or "-g") option to start the
    Apache Tika GUI. You can drag and drop files from
    a normal file explorer to the GUI window to extract
    text content and metadata from the files.

- Tika Pipes mode

    For processing many documents from a directory, S3, GCS, Azure, JDBC, etc.
    Simplest invocation is two directories as args with no other args:
         java -jar tika-app.jar <inputDirectory> <outputDirectory>

Tika Pipes Options:
    -i, --inputDir=<dir>       Input directory
    -o, --outputDir=<dir>      Output directory
    -n, --numClients           Number of forked processes
    --Xmx=<size>               -Xmx for the forked processes, e.g. --Xmx=1g
    -T, --timeoutMillis        Timeout for each parse in milliseconds
    -c, --config=<file>        Tika config file (--config=<file> also accepted)
    -p, --pluginsDir           Plugins directory
    --fileList                 File list (one path per line, relative to --inputDir or absolute)
    --handler                  Handler type: t=text, h=html, x=xml, m=markdown, b=body, i=ignore (default: m)
    --concatenate              Concatenate content from all embedded documents
    --content-only             Output only extracted content (no JSON wrapper); implies --concatenate
    --on-exists                Behavior when an output file exists: exception (default), replace, skip
    -Z                         Recursively unpack all the attachments, too
    --unpack-format=<format>   Output format: REGULAR (default) or FRICTIONLESS
    --unpack-mode=<mode>       Output mode: ZIPPED (default) or DIRECTORY;
                               DIRECTORY when --unpack-format=FRICTIONLESS and no mode is given
    --unpack-include-metadata  Metadata for every extracted file. A Frictionless package
                               carries metadata.json by default; this adds per-file
                               sidecars to REGULAR zip output

Three things in this text need reading with care, and the official page says so too:

  • -T has two meanings. In standard mode it is --text-main. In pipes mode it is --timeoutMillis. -c and -p exist only in pipes mode; in standard mode the configuration is --config=<file>.

  • -z and --extract are listed with the standard options but switch to pipes mode. Their description ("into current directory") is the 3.x wording; in 4.1.0 they always run through Tika Pipes (see Unpacking attachments).

  • The pipes section is a summary. The pipes-mode options are parsed by TikaAsyncCLI with Apache Commons CLI, so --handler t and --handler=t both work, and --fileList takes a value.

Source: the command above, run on the 4.1.0 distribution, and Apache Tika 4.1.x, Tika Command Line Interface.

Standard mode

In standard mode TikaCLI processes its arguments left to right. An option changes the state (the output type, the configuration, the password), and a file name, a URL or - parses that input with the current state and writes the result to standard output. Several inputs in one command are parsed one after the other and their outputs are concatenated.

The order matters in two ways. An option placed after the file name does not apply to it, and --config= must come before the first input (and before -g or -f, as the help says), because the configuration is loaded when the first input is parsed:

tika -d manuscript.docx -t novel.epub   # -d applies to the first file, -t to the second
tika novel.epub -t                      # -t comes too late: the default Markdown is printed

Output:

application/vnd.openxmlformats-officedocument.wordprocessingml.document
Chapter one
It begins at dusk.
# Chapter one

It begins at dusk.

Long options take two dashes and short options are one letter. In 4.1.0 an unknown option fails fast with exit status 1 and a hint, instead of being taken as a URL:

tika -input catalogue.pdf

Output (standard error, stack trace omitted):

Exception in thread "main" java.lang.IllegalArgumentException: Unknown option '-input'. Long-form flags require two dashes (try '--input' instead of '-input'). Run with --help for the full option list.

A missing file is still reported in an unhelpful way, because a name that is not an existing file is tried as a URL. The official integration-test page lists this as a known issue:

Exception in thread "main" java.net.MalformedURLException: no protocol: missing.pdf

Help, version and configuration options

Option Effect in 4.1.0

-?, --help

Prints the usage above. If it appears anywhere on the command line, nothing else runs.

-V, --version

Prints Apache Tika 4.1.0.

-v, --verbose

Raises the Log4j root level to DEBUG.

--config=<tika-config.json>

Loads this JSON configuration with TikaLoader instead of the bundled convenience defaults. Its parsers, detectors and parse-context sections apply, so for example parse-context.exception-reporting bounds the exception values of -j and -J output. A 3.x XML file is rejected; convert it first.

--convert-config-xml-to-json=<input.xml>

Converts the parsers section of a 3.x tika-config.xml to 4.x JSON on standard output, then exits. The Configuration page shows a full conversion and what it does not cover.

With --config=, the parse uses your configuration instead of the convenience defaults. The Bookshelf desk keeps one tika-config.json for the intake service and for its batch runs:

{
  // Bookshelf intake: shared by the desk CLI and the batch runs
  "parsers": [
    { "pdf-parser": { "sortByPosition": true, "extractInlineImages": false } },
    { "default-parser": { "exclude": ["executable-parser"] } }
  ],
  "metadata-filters": [
    { "include-field-metadata-filter": {
        "include": ["Content-Type", "dc:title", "dc:creator", "tk:content"] } }
  ],
  "parse-context": {
    "embedded-limits": { "maxDepth": 5, "maxCount": 200 },
    "timeout-limits":  { "totalTaskTimeoutMillis": 120000, "progressTimeoutMillis": 30000 }
  },
  "pipes": {
    "numClients": 2,
    "forkedJvmArgs": ["-Xmx768m"]
  }
}
tika --config=tika-config.json -j -r manuscript.docx

Output:

{
  "Content-Length" : "1418",
  "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.CompositeParser", "org.apache.tika.parser.DefaultParser", "org.apache.tika.parser.microsoft.ooxml.OOXMLParser", "org.apache.tika.parser.microsoft.ooxml.xwpf.XWPFEventBasedWordExtractor" ],
  "tk:parsed-by-full-set" : [ "org.apache.tika.parser.CompositeParser", "org.apache.tika.parser.DefaultParser", "org.apache.tika.parser.microsoft.ooxml.OOXMLParser" ],
  "tk:resource-name" : "manuscript.docx",
  "zip:detector-zip-file-opened" : "true"
}

The configuration was applied (DefaultParser now appears in tk:parsed-by, because the parsers list is no longer the convenience one), but the include-field-metadata-filter was not applied to this standard-mode -j output: all the keys are printed. The same file in pipes mode, shown later, does apply the filter. This was observed with 4.1.0 and is not described in the docs, so do not rely on metadata filters when you only use standard mode; check the output of your own version.

Output formats

One option chooses what is written for each input. The last one given before the input wins.

Option Output Handler behind it

--md (default)

Markdown of the body. New in 4.x as the default (3.x defaulted to XHTML).

ToMarkdownContentHandler inside a BodyContentHandler

-t, --text

Plain text of the body.

BodyContentHandler on a text writer

-x, --xml

The full XHTML document, including <head> with the metadata as <meta> elements.

XML serializer

-h, --html

The same events serialized as HTML.

HTML serializer with ExpandedTitleContentHandler

-T, --text-main

Plain text of the main content only, boilerplate (navigation, footers) removed.

Boilerpipe handler

-A, --text-all

All text, including the <head> (title) and whitespace, not only the body.

Text over the whole document

-m, --metadata

Metadata only, one key: value line per value, sorted by key.

None (content ignored)

-j, --json

Metadata only, as one JSON object.

None (content ignored)

-y, --xmp

Metadata only, as an XMP packet.

None (content ignored)

-J, --jsonRecursive

A JSON array: the container and every embedded document, each with its metadata and a tk:content field in the format chosen with -t, -x, -h, -m or the default Markdown.

RecursiveParserWrapper

-r, --pretty-print

Indents JSON, XML and XHTML output.

Modifier

--encoding=X

Writes the output in charset X instead of UTF-8. It does not change how the input is decoded.

Modifier

Markdown and plain text of the Bookshelf manuscript:

tika manuscript.docx
tika -t manuscript.docx

Output:

The Lighthouse Keeper

Chapter one begins at dusk.
The Lighthouse Keeper
Chapter one begins at dusk.

XHTML with pretty printing. The <head> carries the metadata; this output is the SAX event stream of the parse, which the Content Handlers & Output Formats page explains:

tika -x -r manuscript.docx

Output (blank lines that the pretty printer inserts between elements removed):

<?xml version="1.0" encoding="UTF-8"?><html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta name="tk:content-type-magic-detected" content="application/vnd.openxmlformats-officedocument.wordprocessingml.document"/>
        <meta name="dc:title" content="The Lighthouse Keeper"/>
        <meta name="dc:creator" content="Ana Torres"/>
        <meta name="tk:resource-name" content="manuscript.docx"/>
        <meta name="zip:detector-zip-file-opened" content="true"/>
        <meta name="Content-Length" content="1418"/>
        <meta name="tk:parsed-by" content="org.apache.tika.parser.CompositeParser"/>
        <meta name="tk:parsed-by" content="org.apache.tika.parser.microsoft.ooxml.OOXMLParser"/>
        <meta name="tk:parsed-by" content="org.apache.tika.parser.microsoft.ooxml.xwpf.XWPFEventBasedWordExtractor"/>
        <meta name="Content-Type" content="application/vnd.openxmlformats-officedocument.wordprocessingml.document"/>
        <title>The Lighthouse Keeper</title>
    </head>
    <body>
        <p>The Lighthouse Keeper</p>
        <p>Chapter one begins at dusk.</p>
    </body>
</html>

-T and -A on the Spanish review page. -T keeps the heading and the first paragraph that boilerpipe judges to be the article; -A prints everything, including the title and the blank lines of the head:

tika -T reviews-es.html

Output:

Reseñas de lectoresReseñas de lectores
El guardián del faro es una novela pausada y luminosa. Ana Torres construye un mundo pequeño, casi doméstico, donde cada noche el protagonista cuenta las olas y espera una carta que nunca llega.

Note the joined title and heading in the first line and the missing second paragraph: main-content extraction is a heuristic for article-like web pages, not a general text extractor. Use -t or --md for documents.

XMP output of the manuscript:

tika -y manuscript.docx

Output:

<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 6.1.11">
  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    <rdf:Description rdf:about=""
        xmlns:dc="http://purl.org/dc/elements/1.1/"
      dc:format="application/vnd.openxmlformats-officedocument.wordprocessingml.document">
      <dc:creator>
        <rdf:Seq>
          <rdf:li>Ana Torres</rdf:li>
        </rdf:Seq>
      </dc:creator>
      <dc:title>
        <rdf:Alt>
          <rdf:li xml:lang="x-default">The Lighthouse Keeper</rdf:li>
        </rdf:Alt>
      </dc:title>
    </rdf:Description>
  </rdf:RDF>
</x:xmpmeta>

--encoding only changes the output charset. The windows-1252 catalogue is decoded correctly either way (Tika detects its encoding); printed as ISO-8859-1 on a UTF-8 terminal, the accented letters show as replacement characters and the euro sign, which ISO-8859-1 cannot represent, becomes ?:

tika -t legacy-catalogue.txt
tika --encoding=ISO-8859-1 -t legacy-catalogue.txt

Output:

Catálogo de primavera: novelas de editoriales independientes. Precio 12€. Disponible en tienda y por correo electrónico a la dirección de la librería.
Cat�logo de primavera: novelas de editoriales independientes. Precio 12?. Disponible en tienda y por correo electr�nico a la direcci�n de la librer�a.

--encoding is for consumers that need a specific output charset, not a way to fix the input decoding. Input encoding is covered on the Character Encoding Detection page.

Metadata: -m, -j and -J

-m prints the metadata sorted by key. The Bookshelf catalogue PDF:

tika -m catalogue.pdf

Output:

Content-Length: 785
Content-Type: application/pdf
access-permission:assemble-document: true
access-permission:can-modify: true
access-permission:can-print: true
access-permission:can-print-faithful: true
access-permission:extract-content: true
access-permission:extract-for-accessibility: true
access-permission:fill-in-form: true
access-permission:modify-annotations: true
dc:creator: Bookshelf Press
dc:format: application/pdf; version=1.4
dc:title: Spring catalogue
pdf:chars-per-page: 67
pdf:contains-damaged-font: false
pdf:contains-non-embedded-font: true
pdf:docinfo:creator: Bookshelf Press
pdf:docinfo:producer: hand-made
pdf:docinfo:title: Spring catalogue
pdf:encrypted: false
pdf:eof-offsets: 785
pdf:has-collection: false
pdf:has-marked-content: false
pdf:has-xfa: false
pdf:has-xmp: false
pdf:incremental-update-count: 0
pdf:num-3d-annotations: 0
pdf:ocr-page-count: 0
pdf:overall-percentage-unmapped-unicode-chars: 0.0
pdf:pdf-version: 1.4
pdf:producer: hand-made
pdf:total-unmapped-unicode-chars: 0
pdf:unmapped-unicode-chars-per-page: 0
tk:content-type-magic-detected: application/pdf
tk:parsed-by: org.apache.tika.parser.CompositeParser
tk:parsed-by: org.apache.tika.parser.pdf.PDFParser
tk:parsed-by-full-set: org.apache.tika.parser.CompositeParser
tk:parsed-by-full-set: org.apache.tika.parser.pdf.PDFParser
tk:resource-name: catalogue.pdf
tk:version-count: 0
xmpTPg:NPages: 1

The 4.x key names (tk:parsed-by, tk:resource-name, kebab-case pdf: keys) are the ones the Metadata page maps to their 3.x names. Scripts written against 3.x output (X-Parsed-By, resourceName, pdf:PDFVersion) need updating.

-j prints the same metadata as one JSON object (compact unless -r is given; multi-valued keys become arrays). -J is the recursive form: a JSON array with one object per document, container first, each with its content in tk:content. On the publisher’s e-mail it lists the e-mail, the zip it carries, the three files in the zip and the image inside the Word file. jq keeps the output readable:

tika -J -t publisher-email.eml \
  | jq -r '.[] | [."tk:embedded-depth" // "0", ."tk:embedded-resource-path" // "(container)", ."Content-Type"] | @tsv'

Output:

0	(container)	message/rfc822
3	/samples.zip/chapter-one.docx/figure1.png	image/png
2	/samples.zip/chapter-one.docx	application/vnd.openxmlformats-officedocument.wordprocessingml.document
2	/samples.zip/catalogue.pdf	application/pdf
2	/samples.zip/cover-note.txt	text/plain; charset=windows-1252
1	/samples.zip	application/zip

The children are listed depth first, and a parent is written after its children (the container is moved to the front). The container object also carries tk:content-handler-type (TEXT here), tk:parse-time-millis and tk:parsed-by-full-set, which lists every parser used anywhere in the tree. --maxEmbeddedDepth=1 stops the recursion after the first level:

tika --maxEmbeddedDepth=1 -J -t publisher-email.eml | jq -r '.[] | ."tk:embedded-resource-path" // "(container)"'

Output:

(container)
/samples.zip

--maxEmbeddedCount=N caps the number of embedded documents in the same way. Both are unlimited by default, set an EmbeddedLimits in the ParseContext, and correspond to parse-context.embedded-limits in tika-config.json. The model behind -J (the "list of metadata" of RecursiveParserWrapper, the same JSON as tika-server’s /rmeta) is described on the Embedded Documents & Recursive Parsing page.

Detection, language and digests

-d runs only the detector and prints the media type. It reads the bytes, so a renamed file is reported as what it is:

tika -d manuscript.docx novel.epub renamed-invoice.png book-cover.jpg

Output:

application/vnd.openxmlformats-officedocument.wordprocessingml.document
application/epub+zip
application/pdf
image/jpeg

renamed-invoice.png is a PDF, and Tika says so. That is the check the intake desk runs before it trusts a file extension (the Media Types & Detection page explains the magic, glob and container steps behind it).

-l parses the text and prints only the detected language. tika-app 4.1.0 bundles the CharSoup detector (tika-langdetect-charsoup), which reports ISO 639-3 codes as they are (tika-server /language converts them to two-letter codes, see Tika Server (REST API)):

tika -l reviews-es.html
tika -l reseñas-fr.txt

Output:

spa
fra

These are three-letter codes, not the two-letter es and fr that the 2.x and 3.x Optimaize detector usually returned. Map them before comparing with stored values. The Language Detection & Translation page covers CharSoup and the short-text pitfalls.

--digest=X adds a digest of the input bytes to the metadata. The accepted values are md2, md5, sha1, sha256, sha384 and sha512 (case-insensitive). It uses the Commons Codec digester, which has no SHA-3; for SHA3-256 and the other SHA-3 variants, configure the Bouncy Castle digester in tika-config.json instead. The digest is a metadata key, so combine it with -j, -m or -J:

tika -j --digest=sha256 catalogue.pdf | jq -r '."tk:digest:SHA-256"'
shasum -a 256 catalogue.pdf

Output:

775dabd42ebe9c7c1ea7cc6c3164a41fcafc51d3541db835f34d1e9a21135385
775dabd42ebe9c7c1ea7cc6c3164a41fcafc51d3541db835f34d1e9a21135385  catalogue.pdf

The key is tk:digest:SHA-256 in 4.x (X-TIKA:digest:SHA256 in 3.x). The intake desk stores it to spot duplicate submissions.

Passwords

--password=X supplies a password for encrypted PDF and Office files. Without it, the members-only catalogue fails with exit status 1:

tika -t members-only.pdf

Output (standard error, stack traces omitted):

Exception in thread "main" org.apache.tika.exception.EncryptedDocumentException: Unable to process: document is encrypted
Caused by: org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException: Cannot decrypt PDF, the password is incorrect

A password on the command line is visible to other users of the machine (in ps output and the shell history). The 4.1.0 source also reads the environment variable TIKA_PASSWORD as the default password, which keeps it off the command line. TIKA_PASSWORD is not mentioned in --help or on the CLI page; it was found in the TikaCLI source and tested:

# MEMBERS_PDF_PASSWORD holds the sample file's test password; in real use fill it from a secret store
export TIKA_PASSWORD="$MEMBERS_PDF_PASSWORD"
tika -t members-only.pdf

Output:

Bookshelf spring catalogue
Forty titles from independent publishers.

--password=X on the command line overrides the environment variable. The password applies to every encrypted document of the run, including embedded ones; for per-file passwords write a PasswordProvider in Java (see Parser API & AutoDetectParser).

Source: Apache Tika 4.1.x, Tika Command Line Interface and the 4.1.0 TikaCLI source (the TIKA_PASSWORD default).

Standard input and URLs

With - or no input, and data waiting on standard input, tika-app parses standard input. With a name that is not an existing file it tries a URL, so file:, http: and https: URLs work:

cat catalogue.pdf | tika -t
cat renamed-invoice.png | tika -d
tika -d "file://$PWD/novel.epub"

Output:

Bookshelf spring catalogue
Forty titles from independent publishers.

application/pdf
application/epub+zip

Standard input has no file name, so detection relies on the bytes alone, which is enough for the renamed PDF. For text formats that need a name hint, pass a file instead. Fetching a URL makes the CLI an HTTP client: run it only on URLs you trust, never on URLs taken from an untrusted document or request (the Security page explains the server-side request forgery risk).

Source: the Description section of --help above.

Listing parsers, detectors and media types

The listing options print what the distribution contains, then exit. They are the quickest way to find the friendly name a component has in tika-config.json.

Option Output (4.1.0)

--list-parsers

The parser tree, starting at AutoDetectParser (91 lines on the 4.1.0 distribution).

--list-parser-names

class-name<TAB>friendly-name, one parser per line (88 lines). The friendly name is the JSON key.

--list-parser-details

Each parser with the media types it supports.

--list-parser-details-apt

The same in Maven APT format.

--list-parser-details-adoc

The same in AsciiDoc. Used to regenerate the official supported-formats page.

--list-detectors

The detector tree under DefaultDetector.

--list-detector-names

class-name<TAB>friendly-name for the detectors.

--list-met-models

Meant to list the metadata models and their keys. On 4.1.0 it printed a single line, Serializable, and nothing useful; use the Javadoc of the org.apache.tika.metadata package instead.

--list-supported-types

Every known media type with its aliases, supertype and parser (3756 lines).

--compare-file-magic=<dir>

Compares Tika’s media types with the magic directory of the file(1) tool.

tika --list-parser-names | grep -E 'pdf|ooxml|csv'
tika --list-detector-names
tika --list-supported-types | grep -A3 '^application/pdf$'

Output:

org.apache.tika.parser.csv.TextAndCSVParser	text-and-csv-parser
org.apache.tika.parser.microsoft.ooxml.OOXMLParser	ooxml-parser
org.apache.tika.parser.microsoft.ooxml.xwpf.ml2006.Word2006MLParser	word-2006-ml-parser
org.apache.tika.parser.pdf.PDFParser	pdf-parser
org.apache.tika.detect.apple.BPListDetector	bp-list-detector
org.apache.tika.detect.gzip.GZipSpecializationDetector	gzip-specialization-detector
org.apache.tika.detect.image.RawTiffDetector	raw-tiff-detector
org.apache.tika.detect.microsoft.POIFSContainerDetector	poifs-container-detector
org.apache.tika.detect.mkv.MatroskaDetector	matroska-detector
org.apache.tika.detect.ogg.OggDetector	ogg-detector
org.apache.tika.detect.ole.MiscOLEDetector	misc-ole-detector
org.apache.tika.detect.zip.DefaultZipContainerDetector	default-zip-container-detector
application/pdf
  alias:     application/x-pdf
  supertype: application/octet-stream
  parser:    org.apache.tika.parser.pdf.PDFParser

The lists reflect the machine, not only the jars. On the host used for this page Tesseract was not installed, so TesseractOCRParser (present in lib/tika-parser-ocr-module-4.1.0.jar) did not appear in --list-parsers, and the startup log said "text recognizers: none found among the loaded parsers". Install Tesseract, as the OCR with Tesseract page describes, and the parser appears.

The GUI

-g / --gui opens org.apache.tika.gui.TikaGUI, a Swing window, and so does running the jar with no arguments and nothing on standard input (double-clicking the jar on a desktop does that). It is the tool the Tika in Action book introduces in chapter 2, and it has changed little since: you drop a file on the window, or use File > Open…​ or File > Open URL…​, and the View menu switches between six views of the same parse.

View menu item What it shows

Metadata

The metadata, as with -m.

Formatted text

The XHTML rendered as HTML, as with -h.

Plain text

The body text, as with -t.

Main content

The boilerpipe main content, as with -T.

Structured text

The XHTML source, as with -x.

Recursive JSON

The list of metadata of the container and its embedded documents, as with -J.

--config= placed before -g is passed to the GUI, so you can inspect a file with the production configuration:

tika --config=tika-config.json --gui

The GUI parses in its own JVM, with no fork, so a hostile file can hang or crash the window, and it needs a desktop session (on a server without a display, Swing cannot open the window; this was not tested here). The GUI was not opened for this page; the menu items above were read from the 4.1.0 TikaGUI source.

Fork mode

-f / --fork keeps the standard-mode command line but runs the parse in a separate JVM through PipesForkParser, the 4.x replacement of the removed ForkParser. If the parser crashes the JVM, runs out of memory or hangs, the forked JVM dies or is killed and the CLI reports it instead of dying itself. That is the right way to look at a file you do not trust, such as an attachment that has already crashed a worker.

Option Default Meaning

-f, --fork

off

Parse in a forked JVM.

--task-timeout=<ms>

3600000 (1 hour)

Total budget per file. The fork is killed when it runs out. Same default as TimeoutLimits in the library and in pipes.

--progress-timeout=<ms>

120000 (2 minutes)

Stall detector: the fork is killed as hung if it makes no progress for this long.

--fork-jvm-args=<args>

none (heap auto-sized)

Comma-separated JVM arguments for the fork, for example --fork-jvm-args=-Xmx512m,-Dsome.prop=value.

--fork-plugins-dir=<dir>

the distribution’s plugins/

Directory with plugin zips for the fork.

--fork-timeout=<ms>

removed

Removed in 4.0. Using it is an error that names the two replacements.

tika -f -t catalogue.pdf

Output (the 35 empty lines before the title are shortened to one):

Spring catalogue


Bookshelf spring catalogue
Forty titles from independent publishers.

The run took about 12.7 seconds of wall time against 6.3 seconds without -f, because a second JVM starts and loads the parsers again. The log on standard error shows the fork being sized and started:

INFO  [main] 22:53:22,064 org.apache.tika.pipes.core.PipesParser Using per-client server mode with 1 clients
INFO  [main] 22:53:22,066 org.apache.tika.pipes.core.PerClientServerManager pipes-cpu-sizing: hostCores=8, numClients=1, parentReserved=2, autoCap=slice=6, heap=MaxRAMPercentage=60
INFO  [main] 22:53:23,030 [fork] pipesClientId=0 org.apache.tika.pipes.core.server.PipesServer forked JVM max heap: 14752 MB
INFO  [main] 22:53:28,472 org.apache.tika.pipes.core.PipesClient clientId=0: server successfully started

Without --fork-jvm-args, the fork’s heap is auto-sized to 60 % of the machine’s memory (MaxRAMPercentage=60); on a shared machine set an explicit -Xmx. The sizing rules are on the official "Forked-JVM CPU and Heap Sizing" page.

Fork mode is not a drop-in copy of standard mode in 4.1.0. Reading the source and running it showed these differences:

  • The output comes from a PipesForkParser result in CONCATENATE parse mode (or RMETA with -J), so -t uses the full text handler: the title from the <head> appears, with the blank lines, as above.

  • -T maps to the BODY handler, not to boilerpipe. -A, -d, -l and -y map to the IGNORE handler and print nothing useful; use them without -f.

  • --password= and TIKA_PASSWORD were not passed to the fork: -f --password=reader-pw -t members-only.pdf printed nothing, and -f -j showed tk:exception:container-exception with the "password is incorrect" error. The parse inside the fork also used its own defaults rather than the parsers of --config=, as far as the source shows (the fork configuration is built from the timeouts, JVM arguments, plugin directory and embedded limits only).

  • The result metadata includes tk:source-path with the absolute path of the input and tk:content-handler-type.

So use fork mode for what it is for (looking safely at one dangerous file) and pipes mode for batches. For production isolation inside a service, use PipesForkParser from Java (see Tika Pipes) or tika-server, whose parsing endpoints always fork.

Tika Pipes mode

Pipes mode is the batch mode of 4.x. It replaces the 1.x to 3.x tika-batch mode of tika-app (-bc, -numConsumers, -maxRestarts and the rest), which was removed with the tika-batch module. tika-app hands the arguments to the tika-async-cli module (TikaAsyncCLI), which writes a temporary tika-config.json from a template, merges your --config into it, applies the command-line overrides and runs an AsyncProcessor: a file-system pipes iterator lists the input files, a pool of forked JVMs parses them, and a file-system emitter writes one output file per input. The Tika Pipes page explains those roles; this section covers the command line.

How pipes mode is activated

tika-app 4.1.0 enters pipes mode when any of these holds (checked before anything else, except --help):

  • exactly two arguments and the first is an existing directory (tika in/ out/);

  • the last two arguments are an existing directory followed by a directory or a path that does not exist yet, with options in front;

  • a single argument ending in .json, which is read as a complete pipes configuration;

  • any of -i, --inputDir (in fact any argument starting with --input), -o, --outputDir (any argument starting with --output), --fileList, -z, -Z, --extract, --extract-dir, -a or --async.

When it switches, it prints a one-line banner on standard error, so you can tell which path ran:

tika-app: running in Tika Pipes mode (async dispatch via -a/--async); use --help for options.

Source: Apache Tika 4.1.x, Tika Command Line Interface, How Pipes mode is activated and the 4.1.0 TikaCLI.testForAsync source.

The simplest run: two directories

The Bookshelf intake folder holds six of the sample files. Two directory arguments and nothing else parse them all:

ls intake
tika intake out
ls out

Output (standard error shortened to the banner, the sizing line and the summary):

catalogue.pdf  legacy-catalogue.txt  manuscript.docx  novel.epub  publisher-email.eml  renamed-invoice.png
tika-app: running in Tika Pipes mode (async dispatch via -a/--async); use --help for options.
INFO  [main] 22:54:40,643 org.apache.tika.pipes.core.PerClientServerManager pipes-cpu-sizing: hostCores=8, numClients=1, parentReserved=2, autoCap=slice=6, heap=user-set in forkedJvmArgs
INFO  [main] 22:54:47,270 tika.pipes Successfully finished processing 6 files in 6653 ms
catalogue.pdf.json  legacy-catalogue.txt.json  manuscript.docx.json  novel.epub.json  publisher-email.eml.json  renamed-invoice.png.json

Each output file is the RMETA list of metadata (the -J format) of one input, with Markdown content. For the manuscript:

jq . out/manuscript.docx.json

Output:

[
  {
    "tk:source-path": "manuscript.docx",
    "tk:content-type-parser-override": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    "dc:creator": "Ana Torres",
    "tk:resource-name": "manuscript.docx",
    "tk:content": "The Lighthouse Keeper  \n\nThe Lighthouse Keeper\n\nChapter one begins at dusk.\n",
    "tk:parsed-by": [
      "org.apache.tika.parser.CompositeParser",
      "org.apache.tika.parser.microsoft.ooxml.OOXMLParser",
      "org.apache.tika.parser.microsoft.ooxml.xwpf.XWPFEventBasedWordExtractor"
    ],
    "tk:content-type-magic-detected": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    "dc:title": "The Lighthouse Keeper",
    "tk:parse-time-millis": "3",
    "tk:parsed-by-full-set": [
      "org.apache.tika.parser.CompositeParser",
      "org.apache.tika.parser.microsoft.ooxml.OOXMLParser"
    ],
    "zip:detector-zip-file-opened": "true",
    "tk:embedded-depth": "0",
    "Content-Length": "1418",
    "tk:content-handler-type": "MARKDOWN",
    "Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
  }
]

tk:source-path is the path relative to the input directory, which keeps the output portable. publisher-email.eml.json holds six objects (the e-mail and its five embedded documents), as with -J. The renamed PDF’s file is renamed-invoice.png.json with "Content-Type": "application/pdf" inside: the output name follows the input name, the content follows the bytes.

The output directory mirrors the input tree (subdirectories included) and the file name is the input name plus .json.

Pipes-mode options

Option Default Meaning

-i, --inputDir=<dir>

none

Input directory; a single file also works (its parent becomes the base path). The long form is --inputDir, not --input.

-o, --outputDir=<dir>

./output (or . for a single input file)

Output directory. The long form is --outputDir.

--fileList=<file>

none

Text file with one path per line, relative to --inputDir or absolute. Only those files are processed.

--handler=<X>

m

Content handler: t text, h HTML, x XHTML, m Markdown, b body, i ignore (metadata only).

--concatenate

off

Parse mode CONCATENATE: one metadata object per input, with the content of all embedded documents joined into its tk:content.

--content-only

off

Parse mode CONTENT_ONLY: write only the content, no JSON wrapper and no metadata; the output extension follows the handler (.txt, .md, .html, .xml). Implies --concatenate.

--on-exists=<mode>

exception

What to do when an output file exists: exception, replace or skip. Also applied to a configured file-system-jsonl-reporter (skip becomes APPEND there).

-n, --numClients=<N>

2 (1 in the bare two-directory form)

Number of forked JVMs that parse in parallel. A pipes.numClients in --config is kept when -n is not given.

--Xmx=<size>

unset: heap auto-sized (-Xmx1g in the bare form)

-Xmx of each forked JVM, for example --Xmx=1g. Merged into pipes.forkedJvmArgs, replacing an existing -Xmx and keeping the other arguments. A long flag despite its look; there is no -X option.

-T, --timeoutMillis=<ms>

config or library default (30000 in the bare form)

Total task timeout per input. Sets parse-context.timeout-limits.totalTaskTimeoutMillis, merged into any timeout-limits of the config.

-c, --config=<file>

the built-in template

A tika-config.json merged over the template. Its top-level sections replace the template’s.

-p, --pluginsDir=<dir>

the distribution’s plugins/

Where the pipes plugins (fetchers, emitters, iterators) are loaded from.

-a, --async

n/a

Forces pipes mode. Redundant with any of the triggers above.

-z, --extract, -Z, --extract-dir=<dir>, --unpack-format, --unpack-mode, --unpack-include-metadata

off

Unpacking, see Unpacking attachments.

The bare-form defaults (one client, -Xmx1g, a 30-second task timeout) were read from the 4.1.0 TikaAsyncCLI source; the numClients=1 in the sizing line above confirms the client count. The other defaults are from the official CLI page and were confirmed by the runs below.

Some older notes list an --unpack pipes option. tika-app 4.1.0 has no option of that name: unpacking is -z / -Z with the --unpack-format, --unpack-mode and --unpack-include-metadata modifiers, which the 4.1.0 --help lists. The parse mode behind them is UNPACK, and the configuration section is unpack-config.

Text for the search index: --handler and --content-only

The intake service feeds plain text to its search index. Two workers with 512 MB each and a one-minute budget per file:

tika -i intake -o text-out --handler t --content-only -n 2 --Xmx=512m -T 60000
ls text-out

Output (standard error shortened):

INFO  [main] 22:54:59,271 org.apache.tika.pipes.core.async.AsyncProcessor Using per-client server mode with 2 workers
INFO  [main] 22:54:59,274 org.apache.tika.pipes.core.PerClientServerManager pipes-cpu-sizing: hostCores=8, numClients=2, parentReserved=2, autoCap=slice=3, heap=user-set in forkedJvmArgs
INFO  [main] 22:55:06,956 tika.pipes Successfully finished processing 6 files in 7729 ms
catalogue.pdf.txt  legacy-catalogue.txt.txt  manuscript.docx.txt  novel.epub.txt  publisher-email.eml.txt  renamed-invoice.png.txt

Each file holds only the text. The text handler includes the <head> title, so each file starts with blank lines and the title; for the manuscript, after 14 empty lines:

The Lighthouse Keeper

The Lighthouse Keeper
Chapter one begins at dusk.

--handler b (body) leaves out the head, and --handler m (the default) gives Markdown, which most chunkers for search and RAG handle better. --concatenate without --content-only keeps the metadata and puts all the embedded text into the container’s tk:content, a good shape for "one search document per submission".

Re-runs, --on-exists and the exit status

Run the same command again and every emit fails, because the output files exist and the default is exception:

tika -i intake -o text-out --handler t --content-only
echo "exit status: $?"

Output (standard error, one of six messages shown, stack traces and paths shortened):

WARN  [pool-2-thread-1] 22:55:20,790 [fork] pipesClientId=0 org.apache.tika.pipes.core.server.EmitHandler emit exception
java.io.IOException: Output already exists (onExists=EXCEPTION, not overwritten): .../text-out/manuscript.docx.txt. Use an empty output dir, delete the file, or set onExists to REPLACE or SKIP.
INFO  [main] 22:55:22,047 tika.pipes Successfully finished processing 6 files in 9213 ms
exit status: 0

Two lessons. First, choose the policy: --on-exists skip resumes an interrupted batch (only missing outputs are written), --on-exists replace reprocesses everything. Second, the exit status is 0 and the summary says "Successfully finished" even though nothing was written. In pipes mode a per-file failure (a parse exception, a timeout, a crash of a fork, an emit failure) is recorded for that file and the batch continues; the command only fails for configuration and startup errors. A script must check the outputs, the log, or a configured reporter (the file-system-jsonl-reporter writes one status line per file), not just $?.

This run also shows that -n defaults to 2 outside the bare form: the warnings came from pipesClientId=0 and pipesClientId=1 without any -n.

A file list and a configuration file

--fileList processes only the listed files, which suits a "reprocess these two" request from the editors:

printf 'manuscript.docx\nnovel.epub\n' > batch.txt
tika -i intake --fileList batch.txt -o listed --handler t
ls listed

Output:

manuscript.docx.json  novel.epub.json

-c (or --config=) merges the Bookshelf tika-config.json shown earlier into the pipes template. Its pipes section sets two clients and -Xmx768m; the --Xmx=1g on the command line replaces the -Xmx and keeps the rest:

tika -i intake -o cfg-out -c tika-config.json --Xmx=1g
jq . cfg-out/manuscript.docx.json

Output (standard error shortened):

INFO  [main] 22:57:03,054 org.apache.tika.pipes.core.async.AsyncProcessor Using per-client server mode with 2 workers
INFO  [main] 22:57:10,207 tika.pipes Successfully finished processing 6 files in 7193 ms
[
  {
    "dc:title": "The Lighthouse Keeper",
    "dc:creator": "Ana Torres",
    "tk:content": "The Lighthouse Keeper  \n\nThe Lighthouse Keeper\n\nChapter one begins at dusk.\n",
    "Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
  }
]

Here the include-field-metadata-filter of the file was applied: only the four listed keys remain. The merge is shallow, so a parsers section in your file replaces the template’s parsers list as a whole (including its convenience settings for PDF and Office), and a fetchers or emitters section replaces the template’s file-system ones. -i and -o are applied after the merge, to every file-system-fetcher, file-system-pipes-iterator and file-system-emitter, whatever their ids.

Defaults of pipes mode

The template that pipes mode starts from (config-template.json in tika-async-cli-4.1.0.jar) is worth knowing, because everything you do not override comes from it:

{
  "content-handler-factory": {
    "basic-content-handler-factory": { "type": "TEXT", "writeLimit": -1, "throwOnWriteLimitReached": true }
  },
  "parsers": [
    { "default-parser": {} },
    { "pdf-parser": { "extractActions": true, "extractInlineImages": true,
                      "extractIncrementalUpdateInfo": true, "parseIncrementalUpdates": true } },
    { "ooxml-parser": { "includeDeletedContent": true, "includeMoveFromContent": true, "extractMacros": true } },
    { "office-parser": { "extractMacros": true } }
  ],
  "fetchers": { "fsf": { "file-system-fetcher": { "basePath": "FETCHER_BASE_PATH", "extractFileSystemMetadata": false } } },
  "emitters": { "fse": { "file-system-emitter": { "basePath": "EMITTER_BASE_PATH", "fileExtension": "json", "onExists": "EXCEPTION" } } },
  "pipes-iterator": { "file-system-pipes-iterator": { "basePath": "FETCHER_BASE_PATH", "countTotal": true, "fetcherId": "fsf", "emitterId": "fse" } },
  "pipes": { "parseMode": "RMETA" },
  "plugin-roots": "PLUGIN_ROOTS"
}

(The JSON was reformatted to fit; the placeholders are replaced at run time by the -i, -o and plugin paths.) The TEXT handler type is overridden per file by --handler, whose default is Markdown. There is no write limit (writeLimit: -1) and the convenience PDF and Office settings are on. For a production batch, pass your own configuration with an output-limits section and the limits of the Robustness, Limits & Timeouts page.

Source: the config-template.json resource of the 4.1.0 tika-async-cli jar in the distribution’s lib/ directory.

A complete pipes configuration as the only argument

A single .json argument runs the pipeline it describes. This is how the Bookshelf nightly job is defined, with named fetcher and emitter ids and absolute paths:

{
  "fetchers": {
    "intake": { "file-system-fetcher": { "basePath": "/srv/bookshelf/intake" } }
  },
  "emitters": {
    "extracted": { "file-system-emitter": {
        "basePath": "/srv/bookshelf/extracted", "fileExtension": "json", "onExists": "REPLACE" } }
  },
  "pipes-iterator": {
    "file-system-pipes-iterator": {
        "basePath": "/srv/bookshelf/intake", "fetcherId": "intake", "emitterId": "extracted" }
  },
  "pipes": { "numClients": 2, "parseMode": "RMETA", "forkedJvmArgs": ["-Xmx768m"] },
  "parse-context": {
    "timeout-limits": { "totalTaskTimeoutMillis": 120000, "progressTimeoutMillis": 30000 }
  },
  "plugin-roots": "/opt/tika-app-4.1.0/plugins"
}
tika bookshelf-nightly.json

Output (standard error shortened):

tika-app: running in Tika Pipes mode (async dispatch via -a/--async); use --help for options.
INFO  [main] 22:57:20,432 org.apache.tika.pipes.core.async.AsyncProcessor Using per-client server mode with 2 workers
INFO  [pool-2-thread-3] 22:57:26,121 org.apache.tika.pipes.core.PipesClient clientId=1: server successfully started
INFO  [pool-2-thread-2] 22:57:26,121 org.apache.tika.pipes.core.PipesClient clientId=0: server successfully started
INFO  [main] 22:57:27,558 tika.pipes Successfully finished processing 6 files in 7153 ms

In this form nothing is merged and no template applies: the file must name a pipes-iterator, and plugin-roots is required. Without it the run stops with TikaConfigException: plugin-roots must be specified. The paths are shown as /srv/bookshelf/.... Replacing the file-system fetcher with S3, GCS, Azure Blob or JDBC (after adding their plugin zips to plugins/) uses the same command, which is what the help means by "S3, GCS, Azure, JDBC, etc."; those plugins were not run for this page.

Unpacking attachments

-z and -Z write the embedded files' bytes, not their text. Both run in pipes mode (parse mode UNPACK) and accept a directory or a single file. -Z extracts recursively; -z is documented as "direct attachments (depth 1)" and is implemented as an embedded depth limit of 2.

Option Meaning

-z, --extract

Extract direct attachments.

-Z

Extract all attachments recursively.

--extract-dir=<dir>

Output directory for the bytes; the same as -o.

--unpack-format=<format>

REGULAR (default) or FRICTIONLESS (a Frictionless Data Package with datapackage.json).

--unpack-mode=<mode>

ZIPPED or DIRECTORY. DIRECTORY when --unpack-format=FRICTIONLESS is given without a mode.

--unpack-include-metadata

Metadata for every extracted file: a Frictionless package carries metadata.json anyway; this adds a .metadata.json sidecar per file to REGULAR zip output.

The publisher’s e-mail, recursively:

mkdir mail && cp intake/publisher-email.eml mail/
tika -Z -i mail -o unpacked
find unpacked -type f | sort

Output:

unpacked/publisher-email.eml-embed/00000001.zip
unpacked/publisher-email.eml-embed/00000002.docx
unpacked/publisher-email.eml-embed/00000003.png
unpacked/publisher-email.eml-embed/00000004.pdf
unpacked/publisher-email.eml-embed/00000005.bin
unpacked/publisher-email.eml.json

The files are named <input>-embed/<8-digit id>.<suffix>, with the suffix chosen from the detected type, and the .json next to them is the usual list of metadata that maps each id to its original name. 00000001.zip is samples.zip itself, and 00000005.bin is notes/cover-note.txt, a windows-1252 text file that received .bin in this run. Read the names from the metadata, not from the suffixes. The CLI keeps this naming whatever unpack-config your --config sets for suffixStrategy, zeroPadName, embeddedIdPrefix or keyBaseStrategy; the other unpack-config settings apply, and "includeOriginal": true there is the only way to also copy the source document into the output.

-z with a single file. The options go before the file: --extract-dir is rewritten to -o, and Commons CLI stops reading options at the first positional argument, so tika -z mail/publisher-email.eml --extract-dir=shallow fails with TikaConfigException: Can't have more than 2 unknown args.

tika -z --extract-dir=shallow mail/publisher-email.eml
find shallow -type f | sort

Output:

shallow/publisher-email.eml-embed/00000001.zip
shallow/publisher-email.eml-embed/00000002.docx
shallow/publisher-email.eml-embed/00000003.pdf
shallow/publisher-email.eml-embed/00000004.bin
shallow/publisher-email.eml.json

In 4.1.0, -z on the e-mail wrote the zip and the three files inside it, and left out only the image inside the Word file. The zip’s entries are one level deeper than "direct attachments of the e-mail", so the depth accounting counts differently from what the description suggests. Check with your own files when the depth matters, or set parse-context.embedded-limits.maxDepth explicitly.

A Frictionless Data Package of the same e-mail:

tika -Z --unpack-format=FRICTIONLESS -i mail -o fl
find fl -type f | sort
jq '.resources[] | {path, mediatype, name}' fl/publisher-email.eml/datapackage.json

Output:

fl/publisher-email.eml.json
fl/publisher-email.eml/datapackage.json
fl/publisher-email.eml/metadata.json
fl/publisher-email.eml/unpacked/00000001.zip
fl/publisher-email.eml/unpacked/00000002.docx
fl/publisher-email.eml/unpacked/00000003.png
fl/publisher-email.eml/unpacked/00000004.pdf
fl/publisher-email.eml/unpacked/00000005.bin
{ "path": "unpacked/00000003.png", "mediatype": "image/png", "name": "figure1.png" }
{ "path": "unpacked/00000002.docx", "mediatype": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "name": "chapter-one.docx" }
{ "path": "unpacked/00000004.pdf", "mediatype": "application/pdf", "name": "catalogue.pdf" }
{ "path": "unpacked/00000005.bin", "mediatype": "text/plain", "name": "notes/cover-note.txt" }
{ "path": "unpacked/00000001.zip", "mediatype": "application/zip", "name": "samples.zip" }

Each resource in datapackage.json also has bytes and a hash (sha256:...), so a consumer can verify what it reads. The name is the original name from the e-mail or the archive; it is untrusted input. Never use it as a path on disk without sanitizing it (a name such as ../../etc/cron.d/x is a classic "zip slip"). The 8-digit file names Tika writes are safe; that is one reason it writes them. The official home page says the Frictionless format is planned to become the default in 4.2.0.

Removed and changed options

The command line changed more between 3.x and 4.x than in any earlier release. 4.1.0 rejects the removed options with an error (exit status 1) instead of misreading them.

Old option Removed in What to use / what 4.1.0 does

-s, --server, -p, --port (server mode)

2.0 (3.x printed an error naming the wiki page)

Run tika-server (see Tika Server (REST API)). 4.1.0: Unknown option '--server'. In 4.x -p exists only in pipes mode, as --pluginsDir.

-c<uri>, --client=<uri> (network client, NetworkParser)

4.0

Call tika-server over HTTP. The official page explains that it sent raw sockets, including a bare telnet: scheme, to any user-supplied host with no authentication or TLS. 4.1.0: Unknown option '--client=...'. In 4.x -c is the pipes-mode --config.

-eX (inline encoding)

4.0

--encoding=X. 4.1.0: Unknown option '-eUTF-8'. Long-form flags require two dashes ...

-pX (inline password)

4.0

--password=X or TIKA_PASSWORD. Same error as -eX.

Batch mode (-bc, -numConsumers, -maxRestarts, -timeoutThresholdMillis, -includeFilePat, -excludeFilePat, -maxFileSizeBytes, -JXmx4g, -JD...)

4.0, with tika-batch

Pipes mode: -n, --Xmx, -T, --fileList, --config with pipes and parse-context. Include and exclude patterns have no CLI flag; filter the file list or configure the iterator.

--fork-timeout=<ms>

4.0

--task-timeout=<ms> and --progress-timeout=<ms>. The error message says so.

--config=<tika-config.xml>

4.0 (format)

--config=<tika-config.json>; convert with --convert-config-xml-to-json=.

--dump-minimal-config, --dump-current-config, --dump-static-config, --dump-static-full-config

4.0

Not implemented in 4.x (commented out in the source, listed as a known issue on the integration-test page). Keep your tika-config.json under version control instead.

--container-aware, --container-aware-detector

earlier

Accepted and ignored: container-aware detection is always on.

-x as the default output

4.0

The default is --md. Add -x to scripts that expect XHTML.

-f through ForkParser

4.0

-f now uses PipesForkParser, with the new timeout flags and the differences listed under fork mode.

-a with an XML async config

4.0

-a forces pipes mode; a JSON pipes config is passed with -c or as the only argument.

The removed options, run on 4.1.0 (standard error, stack traces omitted):

tika --server
tika --client=http://localhost:9998
tika -eUTF-8 -t catalogue.pdf
tika --fork-timeout=1000 -f catalogue.pdf

Output:

Exception in thread "main" java.lang.IllegalArgumentException: Unknown option '--server'. Run with --help for the full option list.
Exception in thread "main" java.lang.IllegalArgumentException: Unknown option '--client=http://localhost:9998'. Run with --help for the full option list.
Exception in thread "main" java.lang.IllegalArgumentException: Unknown option '-eUTF-8'. Long-form flags require two dashes (try '--eUTF-8' instead of '-eUTF-8'). Run with --help for the full option list.
Exception in thread "main" java.lang.IllegalArgumentException: --fork-timeout was removed in 4.0 because its meaning was ambiguous (total budget vs. stall detector). Use --task-timeout=<ms> for the total per-file budget and/or --progress-timeout=<ms> for the stall detector instead.

(The two-dash suggestion for -eUTF-8 is the generic hint; the right replacement is --encoding=UTF-8.)

From the book to 4.x

Tika in Action (chapter 2) introduced tika-app as a single runnable jar of Tika 1.0 with three uses: a command-line extractor, the GUI and a simple socket server (--server). The changes since then, in short:

Topic 1.x (book) 2.x 3.x 4.x

Distribution

One fat jar

One fat jar

One fat jar

Zip: thin jar, lib/, plugins/

Java

as stated in the book (Tika 1.0 era); 8 by 1.28

8

11

17

Default output

XHTML (-x)

XHTML

XHTML (--md added in 3.x)

Markdown (--md)

Configuration

--config= XML

XML

XML

JSON (TikaLoader), XML converter

Server

--server in tika-app

removed (error)

removed (error)

removed (unknown option)

Network client

-c<uri>, --client=

kept

kept

removed

Inline -eX, -pX

yes

yes

yes

removed

Batch

tika-batch (-i/-o, -bc)

tika-batch, -a async (XML)

tika-batch, -a async

Tika Pipes mode only

Fork mode

-f, ForkParser

ForkParser

ForkParser

PipesForkParser, two timeouts

Metadata keys

1.x names

X-TIKA:*

X-TIKA:*

tk: namespace

Language detection

n-gram profiles, 2-letter codes

Optimaize

Optimaize

CharSoup, ISO 639-3 codes

The version cells for 1.x and 2.x were taken from the --help source at the 1.28.5 and 2.9.4 tags and from the Introduction & Architecture page; none of those versions was run for this page.

When to use tika-app, and when not

Task Use

Look at one file: its type, its metadata, its text

Standard mode. -d, -m / -j, --md / -t.

Look at one file that may be hostile

Fork mode -f, or standard mode in a throwaway container.

Check what your service will see

Standard mode with --config= set to the service’s file (and remember the fork-mode and metadata-filter differences above).

Find a component’s JSON name

--list-parser-names, --list-detector-names.

Convert a 3.x XML configuration

--convert-config-xml-to-json=.

Process a folder once, or a nightly batch

Pipes mode, with a configuration file and a reporter.

Parse on request from an application

Not tika-app: use tika-server, tika-grpc, or PipesForkParser in your JVM. Starting a JVM per document costs seconds (6.3 s for the 785-byte catalogue on the verification machine, most of it loading parsers).

Run Tika in containers

The apache/tika images run tika-server, not tika-app (see Docker & Kubernetes Deployment).

Memory, performance and robustness

  • JVM start-up dominates. Every standard-mode call starts a JVM and loads more than a hundred parsers: about 6 seconds for a tiny PDF here. Put several files in one call (tika -t a.pdf b.docx) or use pipes mode, which starts its forks once and reuses them.

  • Standard mode has no isolation. A parser bug, a zip bomb or an endless loop affects the CLI JVM itself. Set -Xmx before -jar, use -f for unknown files, and do not run standard mode on untrusted input in a loop.

  • Fork and pipes heap. Without --Xmx / --fork-jvm-args, each fork’s heap is sized from the machine’s memory (60 % in the fork-mode run above). With -n 4 on a shared host, set --Xmx so that n times the heap plus the parent fits.

  • Timeouts. Fork mode: --task-timeout and --progress-timeout. Pipes mode: -T for the total budget, and progressTimeoutMillis in the configuration for the stall detector.

  • Limits. The CLI defaults do not cap the output size (writeLimit: -1 in the pipes template, and unlimited embedded depth and count). Add output-limits and embedded-limits to your configuration, as described on the Robustness, Limits & Timeouts page.

  • Temporary files. Spooling and per-fork temp directories go to java.io.tmpdir (logged at start-up); point it at a volume with enough space for large batches with -Djava.io.tmpdir=... before -jar and in --fork-jvm-args.

Pitfalls and security

  • Pinning. The CLI is in flux for 4.x. Pin the version (tika-app-4.1.0) in scripts and re-read --help when you upgrade.

  • Exit status in pipes mode. 0 even when every file failed; check outputs or a reporter.

  • Option order. --config= before the first file; pipes options before a positional input.

  • -T and -c mean different things in the two modes.

  • Convenience defaults. Standard mode and the pipes template turn on macro extraction, deleted Word text, PDF actions and incremental updates. That is right for inspection and forensics; for a public index, deleted text and macros may be content you do not want to publish. Use your own configuration.

  • Passwords on the command line are visible in process lists and shell history; prefer TIKA_PASSWORD from a secret store, and remember that fork mode did not pass it in 4.1.0.

  • URLs. Passing a URL makes tika-app fetch it. Do not build tika-app command lines from untrusted strings, and quote file names (a file named -input is taken as an option unless it exists).

  • Extracted names are untrusted. Original names in -J output and datapackage.json come from the documents.

  • Hostile files. The CLI parses with the same parsers as the library and has the same exposure to parser vulnerabilities (for example CVE-2025-54988 / CVE-2025-66516 in the PDF module before 3.2.2). Keep the distribution current; the Security page covers the threat model.

What was verified and what was not

Verified by running the official 4.1.0 zip distribution on Java 25 (macOS): the --help and --version output, every standard-mode output format shown, -d, -l, --digest, --password and TIKA_PASSWORD, standard input and file: URLs, the listing options, --maxEmbeddedDepth, fork mode (including the password and handler differences), the removed-option errors, pipes mode with two directories, -i / -o with --handler, --content-only, -n, --Xmx, -T, --fileList, -c, --on-exists (default and skip), a single .json pipes configuration (with and without plugin-roots), and -z, -Z and --unpack-format=FRICTIONLESS.

Read from the 4.1.0 source but not exercised: the bare-form timeout (30 s) and heap (-Xmx1g), --unpack-mode, --unpack-include-metadata, --compare-file-magic, --list-parser-details-apt and -adoc, --fork-plugins-dir, --progress-timeout and --task-timeout actually killing a fork, the GUI and its menus, and pipes plugins other than the file system. Not checked at all: Windows behavior, and the 1.x / 2.x command lines (read from source only).