`karate.read()` silently corrupts .xlsx (and other Office/zip-based) files — binary-extension allowlist is incomplete, and docs contradict the actual behavior
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:
// 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:
- Extend the binary-extension allowlist in
KarateJs.initRead()to includexlsx,xls,docx,doc,pptx,ppt(and ideally other common Office/zip-based formats), or - 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 - 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
- Given this feature file:
Feature: repro Scenario: * def bytes = karate.read('classpath:some-file.xlsx') * match karate.typeOf(bytes) == 'bytes' - When I run it against any
.xlsxfile on the classpath... - I see the
matchstep fail —bytesis 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
Source: karatelabs/karate