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

  1. 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.
  2. 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.
  3. 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.
  4. 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

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


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/6e897739b746885e. Report an issue: GitHub.