#3021·karate

`karate.read()` silently corrupts .xlsx (and other Office/zip-based) files — binary-extension allowlist is incomplete, and docs contradict the actual behavior

Author: arnaldopCreated Aug 21, 2026Updated Aug 26, 2026
Labelsbugfixed

Description

(@ptrthomas, I discovered while using Claude Code to upgrade from Karate v1 to V2. I tried to make sure that it was correct in its findings. The upgraded code works, so at least I was able to verify that.)

karate.read(path) decodes any file whose extension isn't in a small hardcoded allowlist as text via resource.getText(). For binary formats not on that list — notably .xlsx, and presumably .docx/.pptx/other zip-based Office formats — this silently corrupts the bytes (invalid UTF-8 sequences get mangled/replaced), so a file read this way and sent as an HTTP request body arrives at the server broken. This is a silent data-corruption bug, not just a missing-feature gap: read() doesn't throw or warn, it just returns wrong bytes.

The extension allowlist in KarateJs.initRead() is:

java
// Binary file types - return raw bytes (V1 compatibility)
case "pdf", "png", "jpg", "jpeg", "gif", "ico", "mp4", "bin", "zip", "gz", "tar" -> FileUtils.toBytes(resource.getStream());
default -> resource.getText();

.xlsx (and .docx/.pptx) are genuinely binary — they're ZIP archives — but are missing from this list, so they fall into default -> resource.getText().

This also contradicts the docs. The Karate Object API reference for karate.read(path) states:

Read a file with automatic conversion based on extension: JSON, XML, CSV, YAML are parsed; JS files are evaluated; feature files return a callable reference; binary types return byte arrays; everything else returns a string.

This is unqualified — it doesn't say which extensions count as "binary types," so a reader reasonably assumes any genuinely binary file (including .xlsx) is covered. It isn't. The only example given for karate.readAsBytes() is logo.png — already on the allowlist — so nothing in the docs signals that .xlsx needs the explicit readAsBytes() call instead of plain read().

Suggested fix — either:

  1. Extend the binary-extension allowlist in KarateJs.initRead() to include xlsx, xls, docx, doc, pptx, ppt (and ideally other common Office/zip-based formats), or
  2. Detect binary content by sniffing (e.g. ZIP magic bytes PK\x03\x04) rather than a hardcoded extension list, so new formats don't require a code change, or
  3. At minimum, update the karate.read() doc to explicitly enumerate which extensions are treated as binary.

(1) or (2) seem preferable — (3) alone leaves the silent-corruption trap in place for anyone who doesn't read the docs closely enough to notice the list is incomplete.

Workaround: call karate.readAsBytes(path) explicitly instead of karate.read(path) for any binary file type not on the allowlist.

Steps to Reproduce

  1. Given this feature file:
    gherkin
    Feature: repro
    
      Scenario:
        * def bytes = karate.read('classpath:some-file.xlsx')
        * match karate.typeOf(bytes) == 'bytes'
  2. When I run it against any .xlsx file on the classpath...
  3. I see the match step fail — bytes is a string, not a byte array, and its content no longer matches the original file (round-tripped through text decoding).

Real-world trigger: upload an .xlsx via request karate.read(file) to any endpoint, then try to parse it server-side (e.g. with Apache POI) — the file is unreadable.

Expected Behavior

.xlsx is a binary (ZIP-based) format, so karate.read() should return a byte array per the documented behavior, and the bytes should be byte-for-byte identical to the source file.

Actual Behavior

karate.typeOf(bytes) returns 'string' instead of 'bytes'. The content has been decoded as text and no longer matches the original file's bytes — any consumer that expects the real .xlsx (e.g. re-uploading it, or a server parsing it with Apache POI) fails with a generic "file unreadable" / parse error, with no indication the root cause is a lossy text round-trip inside read().

Karate Version

2.0.8 (also reproduced on 2.0.4)

Java Version

25.0.1

Operating System

macOS