{"record":{"id":"92ee0fbea53858ef","repo":"elastic/elasticsearch","slug":"cannot-guess-the-xcontent-type-without-mark-reset","errorCode":null,"errorMessage":"Cannot guess the xcontent type without mark/reset support on ${si.getClass()}","messagePattern":"Cannot guess the xcontent type without mark/reset support on (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/x-content/src/main/java/org/elasticsearch/xcontent/XContentFactory.java","lineNumber":229,"sourceCode":"    }\n\n    /**\n     * Guesses the content type based on the provided input stream without consuming it.\n     *\n     * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type.\n     * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed.\n     * This method is deprecated to prevent usages of it from spreading further without specific reasons.\n     */\n    @Deprecated\n    public static XContentType xContentType(InputStream si) throws IOException {\n        /*\n         * We need to guess the content type. To do this, we look for the first non-whitespace character and then try to guess the content\n         * type on the GUESS_HEADER_LENGTH bytes that follow. We do this in a way that does not modify the initial read position in the\n         * underlying input stream. This is why the input stream must support mark/reset and why we repeatedly mark the read position and\n         * reset.\n         */\n        if (si.markSupported() == false) {\n            throw new IllegalArgumentException(\"Cannot guess the xcontent type without mark/reset support on \" + si.getClass());\n        }\n        si.mark(Integer.MAX_VALUE);\n        try {\n            // scan until we find the first non-whitespace character or the end of the stream\n            int current;\n            do {\n                current = si.read();\n                if (current == -1) {\n                    return null;\n                }\n            } while (Character.isWhitespace((char) current));\n            // now guess the content type off the next GUESS_HEADER_LENGTH bytes including the current byte\n            final byte[] firstBytes = new byte[GUESS_HEADER_LENGTH];\n            firstBytes[0] = (byte) current;\n            int read = 1;\n            while (read < GUESS_HEADER_LENGTH) {\n                final int r = si.read(firstBytes, read, GUESS_HEADER_LENGTH - read);\n                if (r == -1) {","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/x-content/src/main/java/org/elasticsearch/xcontent/XContentFactory.java#L211-L247","documentation":"Thrown by the deprecated XContentFactory.xContentType(InputStream) when the input stream does not support mark()/reset(). Content-type guessing requires peeking at the first non-whitespace bytes and then resetting the stream so the caller can read from the beginning. Without mark/reset support, the peek would consume bytes irreversibly.","triggerScenarios":"Passing a raw SocketInputStream, FileInputStream, or other non-buffered stream that does not implement markSupported(). The guessing algorithm calls si.mark(Integer.MAX_VALUE) and later si.reset(), which would fail or throw on a non-markable stream.","commonSituations":"Reading directly from a network socket or file without buffering. A custom InputStream wrapper that does not delegate mark/reset. Receiving a stream from a library that wraps it in a non-buffered adapter.","solutions":["Wrap the input stream in a BufferedInputStream before passing it to xContentType().","Read the bytes into a byte array first and use the byte[] overload instead.","Avoid the deprecated auto-detection API entirely; read the Content-Type header and specify the type explicitly.","If implementing a custom InputStream, override markSupported() to return true and implement mark/reset correctly."],"exampleFix":"// before — raw socket stream, no mark/reset\nXContentType type = XContentFactory.xContentType(socket.getInputStream());\n\n// after — wrap in BufferedInputStream\nXContentType type = XContentFactory.xContentType(new BufferedInputStream(socket.getInputStream()));","handlingStrategy":"validation","validationCode":"// Before calling xContentType(InputStream), ensure mark/reset is supported\npublic static XContentType safeXContentType(InputStream si) throws IOException {\n    if (!si.markSupported()) {\n        si = new BufferedInputStream(si);\n    }\n    return XContentFactory.xContentType(si);\n}","typeGuard":null,"tryCatchPattern":"try {\n    XContentType type = XContentFactory.xContentType(inputStream);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"mark/reset support\")) {\n        // Retry with buffered stream\n        XContentType type = XContentFactory.xContentType(new BufferedInputStream(inputStream));\n    }\n}","preventionTips":["Always wrap raw streams in BufferedInputStream before passing to xContentType().","Prefer the byte[] overload by reading the stream into a buffer first.","Avoid the deprecated auto-detection API; read Content-Type header and specify type explicitly."],"tags":["xcontent","content-type-detection","deprecated","input-stream","mark-reset"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}