Description
ClickHouseLZ4InputStream.readFully(...) builds its truncated-stream error with
MessageFormat-style placeholders, but ClickHouseUtils.format is a wrapper around
String.format(Locale.ROOT, ...), which only understands %s-style conversions.
String.format leaves {0} / {1} untouched and ignores the extra varargs, so the
message reaches the user as the literal template. The two numbers — how many bytes were
actually read and how many were expected — are the entire diagnostic value of the
message, and they are silently dropped.
Low severity (message quality only, no data or control-flow impact), but it makes a
truncated/interrupted compressed response harder to diagnose in exactly the situation
where the numbers matter.
Steps to reproduce
- Create a
ClickHouseLZ4InputStream over a stream that ends before a full 25-byte LZ4 block header (a truncated or interrupted compressed response).
- Call
read(byte[], int, int).
- Read the message of the resulting
IOException.
Error Log or Exception StackTrace
java.io.IOException: Incomplete read: {0} of {1}
at com.clickhouse.client.api.internal.ClickHouseLZ4InputStream.readFully(ClickHouseLZ4InputStream.java:86)
at com.clickhouse.client.api.internal.ClickHouseLZ4InputStream.refill(ClickHouseLZ4InputStream.java:101)
at com.clickhouse.client.api.internal.ClickHouseLZ4InputStream.read(ClickHouseLZ4InputStream.java:61)
Expected Behaviour
The message names the actual counts, e.g. Incomplete read: 10 of 25.
Code Example
Run against main (be331d4):
import com.clickhouse.client.api.internal.ClickHouseLZ4InputStream;
import com.clickhouse.data.ClickHouseUtils;
import net.jpountz.lz4.LZ4Factory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
public class Repro {
public static void main(String[] args) throws Exception {
System.out.println(ClickHouseUtils.format("Incomplete read: {0} of {1}", 10, 25));
System.out.println(ClickHouseUtils.format("Incomplete read: %s of %s", 10, 25));
byte[] truncated = new byte[10]; // fewer than HEADER_LENGTH (25)
ClickHouseLZ4InputStream lz4 = new ClickHouseLZ4InputStream(
new ByteArrayInputStream(truncated),
LZ4Factory.fastestInstance().fastDecompressor(), 8192);
try {
lz4.read(new byte[64], 0, 64);
} catch (IOException e) {
System.out.println("IOException message: " + e.getMessage());
}
}
}
Observed output:
Incomplete read: {0} of {1}
Incomplete read: 10 of 25
IOException message: Incomplete read: {0} of {1}
The second line shows the same call with %s producing the intended message, so the
wrapper works as documented — only this one call site uses the wrong placeholder syntax.
Root cause
client-v2/src/main/java/com/clickhouse/client/api/internal/ClickHouseLZ4InputStream.java:86
throw new IOException(ClickHouseUtils.format("Incomplete read: {0} of {1}", n, len));
clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseUtils.java:549-551
public static String format(String template, Object... args) {
return String.format(Locale.ROOT, template, args);
}
String.format has no meaning for {0}; it copies the braces through verbatim and
never consumes n or len.
Suggested fix
Change the template to the conversion syntax the wrapper actually implements:
throw new IOException(ClickHouseUtils.format("Incomplete read: %s of %s", n, len));
A repo-wide sweep of main sources found this is the only remaining occurrence of
MessageFormat placeholders in a ClickHouseUtils.format / String.format template, so
the change is self-contained. All other ClickHouseUtils.format call sites already use
%s and must keep their current behaviour.
A negative test on the truncated-header path (assert the message contains the real
counts, not the literal template) would prevent the idiom from reappearing.
Configuration
Environment
ClickHouse Server
- ClickHouse Server version: not required — the failure is entirely client-side string formatting and reproduces without a server.
Found by automated analysis of client-v2 while working on an unrelated change, and
verified by executing the real ClickHouseLZ4InputStream.read(...) entry point rather
than by inspection.
Description
ClickHouseLZ4InputStream.readFully(...)builds its truncated-stream error withMessageFormat-style placeholders, but
ClickHouseUtils.formatis a wrapper aroundString.format(Locale.ROOT, ...), which only understands%s-style conversions.String.formatleaves{0}/{1}untouched and ignores the extra varargs, so themessage reaches the user as the literal template. The two numbers — how many bytes were
actually read and how many were expected — are the entire diagnostic value of the
message, and they are silently dropped.
Low severity (message quality only, no data or control-flow impact), but it makes a
truncated/interrupted compressed response harder to diagnose in exactly the situation
where the numbers matter.
Steps to reproduce
ClickHouseLZ4InputStreamover a stream that ends before a full 25-byte LZ4 block header (a truncated or interrupted compressed response).read(byte[], int, int).IOException.Error Log or Exception StackTrace
Expected Behaviour
The message names the actual counts, e.g.
Incomplete read: 10 of 25.Code Example
Run against
main(be331d4):Observed output:
The second line shows the same call with
%sproducing the intended message, so thewrapper works as documented — only this one call site uses the wrong placeholder syntax.
Root cause
client-v2/src/main/java/com/clickhouse/client/api/internal/ClickHouseLZ4InputStream.java:86clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseUtils.java:549-551String.formathas no meaning for{0}; it copies the braces through verbatim andnever consumes
norlen.Suggested fix
Change the template to the conversion syntax the wrapper actually implements:
A repo-wide sweep of
mainsources found this is the only remaining occurrence ofMessageFormat placeholders in a
ClickHouseUtils.format/String.formattemplate, sothe change is self-contained. All other
ClickHouseUtils.formatcall sites already use%sand must keep their current behaviour.A negative test on the truncated-header path (assert the message contains the real
counts, not the literal template) would prevent the idiom from reappearing.
Configuration
Environment
main@ be331d4 (VERSION 0.11.0-rc1)ClickHouse Server
Found by automated analysis of
client-v2while working on an unrelated change, andverified by executing the real
ClickHouseLZ4InputStream.read(...)entry point ratherthan by inspection.