elastic/elasticsearch · error · XContentParseException
Input does not start with Smile format header
Error message
Input does not start with Smile format header
What it means
Thrown by SmileXContentImpl.validateSmileHeader when a parser is created from an InputStream whose first three bytes do not match the Smile binary format magic header (HEADER_BYTE_1/2/3 = 0x3A 0x29 0x0A, i.e. ':)\n'). The check is skipped only when the stream is entirely empty (length==0). This guards the InputStream overload; the byte[] overload delegates header checking to Jackson's smileFactory.
Source
Thrown at libs/x-content/impl/src/main/java/org/elasticsearch/xcontent/provider/smile/SmileXContentImpl.java:124
@Override
public XContentParser createParser(XContentParserConfiguration config, InputStream is) throws IOException {
return new SmileXContentParser(config, smileFactory.createParser(validateSmileHeader(is)));
}
private static InputStream validateSmileHeader(InputStream is) throws IOException {
PushbackInputStream input = new PushbackInputStream(is, 3);
byte[] header = new byte[3];
int length = input.readNBytes(header, 0, header.length);
input.unread(header, 0, length);
if (length == 0) {
return input;
}
if (length < 3
|| header[0] != SmileConstants.HEADER_BYTE_1
|| header[1] != SmileConstants.HEADER_BYTE_2
|| header[2] != SmileConstants.HEADER_BYTE_3) {
throw new XContentParseException(null, "Input does not start with Smile format header");
}
return input;
}
@Override
public XContentParser createParser(XContentParserConfiguration config, byte[] data, int offset, int length) throws IOException {
try {
return new SmileXContentParser(config, smileFactory.createParser(data, offset, length));
} catch (JsonParseException e) {
throw new XContentParseException(null, e.getMessage(), e);
}
}
@Override
public XContentParser createParser(XContentParserConfiguration config, Reader reader) throws IOException {
return new SmileXContentParser(config, smileFactory.createParser(reader));
}
}View on GitHub (pinned to db6a809a66)
Solutions
- Confirm the bytes are actually Smile: the stream must begin with 0x3A 0x29 0x0A. If you have JSON/CBOR/YAML, select that XContentType instead of SMILE.
- If the content type is unknown, detect it first via XContentType.xContentType(mediaType) or by sniffing the first bytes, then route to the matching createParser.
- When reading from an index/store that may have been written in a different format, verify the stored source contentType metadata rather than assuming Smile.
- Re-acquire the stream from its origin if it may have been consumed or transformed by an intervening filter (gzip, base64) before reaching the parser.
Example fix
// before
XContentParser p = XContentType.SMILE.xContent().createParser(config, in); // in holds JSON
// after
XContentType type = XContentType.xContentType(mediaType); // resolve from header/metadata
try (XContentParser p = type.xContent().createParser(config, in)) { ... } Defensive patterns
Strategy: validation
Validate before calling
byte[] head = is.readNBytes(3); // peek (or use PushbackInputStream)
boolean looksSmile = head.length >= 3 && (head[0] & 0xFF) == 0x3A && (head[1] & 0xFF) == 0x29 && (head[2] & 0xFF) == 0x0A;
if (!looksSmile) { /* pick correct XContentType instead of SMILE */ } Try / catch
try (XContentParser p = XContentType.SMILE.xContent().createParser(config, in)) {
...
} catch (XContentParseException e) {
if (e.getMessage().contains("Smile format header")) {
// re-detect content type and retry with the correct xContent
}
} Prevention
- Always resolve XContentType from the source's media type or by sniffing bytes before selecting a parser.
- Use the cluster's content-type detection helpers rather than hard-coding SMILE.
- In tests, assert the first three bytes when loading Smile fixtures so silent encoding drift fails early.
When it happens
Trigger: Calling XContentType.SMILE.xContent().createParser(config, inputStream) where the stream contains JSON, CBOR, YAML, plain text, or any non-Smile bytes. Also triggered when a content-type sniffing layer picks Smile incorrectly and feeds the wrong bytes, or when a Smile stream is double-decompressed/garbled so the first three bytes are wrong.
Common situations: Mistaking the xcontent type during manual deserialization (e.g. indexing Smile-encoded docs but reading them back as JSON). Content-Type negotiation bugs in REST handlers that map a JSON body to the Smile parser. Tests that hard-code Smile but supply JSON fixtures. Network/proxy corruption that strips or re-encodes the leading bytes.
Related errors
- expected value but got [{token}]
- [{name}] failed to parse object
- [{name}] failed to parse field [{field}]
- Required [{fields}]
- failed to build [{name}] after last required field arrived
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/6e897739b746885e.
Report an issue: GitHub.