Observed
- Create a
JSON column and insert 4 rows with heterogeneous value types across the same JSON
paths (e.g. latency_ms is an array in one row, a string in another).
SELECT the column back using the Native protocol with clickhouse-go v2.
- The driver panics or returns a wire decode error:
panic: runtime error: makeslice: len out of range (in production with large datasets)
read data: failed to decode block: unexpected value N for boolean
clickhouse [process]: unexpected packet N
Expected behaviour
The SELECT should return all rows without error.
Code example
package main
import (
"context"
"fmt"
"log"
"github.com/ClickHouse/clickhouse-go/v2"
)
// rows contains heterogeneous JSON documents. The same key appears with
// different value types across rows (Int64, String, Array, Bool, Float64,
// nested object, timestamp) so that each JSON path acquires multiple
// inferred CH types — the condition that triggers the discriminator
// index shift in the v0 deprecated decoder.
var rows = []string{
`{"latency_ms":[]}`,
`{"cpu_pct":1,"region":"x","request_id":1,"response_times":[],"service":"x","tags":[]}`,
`{"counts":[],"created_at":"x","ended_at":"x","errors":[],"labels":[],"span_ids":[],"started_at":"x","trace_id":"x","user_id":"x"}`,
`{"attachments":[],"config":{"host":"host-149","nested":{"extra":null,"name":"svc-147","parent":null},"value":null},"context":{"name":"ctx-273","parent":null,"root":false},"durations":[],"logged_at":"x","metadata":{"active":true,"ids":[null,null],"ts":"2026-05-04T15:18:01Z"},"metrics":{"primary":{"count":542265,"enabled":true,"parent":null},"samples":[18485,18893],"secondary":{"extra":null,"label":"label-166","parent":null}},"org_id":"x","stack_frames":[],"updated_at":"x"}`,
}
func main() {
conn, err := clickhouse.Open(&clickhouse.Options{
Addr: []string{"127.0.0.1:9002"},
Auth: clickhouse.Auth{Database: "default", Username: "default", Password: "password"},
Protocol: clickhouse.Native,
})
if err != nil {
log.Fatal(err)
}
defer conn.Close()
ctx := context.Background()
if err := conn.Ping(ctx); err != nil {
log.Fatalf("ping: %v", err)
}
exec := func(q string) {
if err := conn.Exec(ctx, q); err != nil {
log.Fatalf("%s\n %v", q, err)
}
}
exec("DROP TABLE IF EXISTS test_json_table")
exec("CREATE TABLE test_json_table (raw JSON(max_dynamic_paths=32)) ENGINE = MergeTree ORDER BY tuple()")
batch, err := conn.PrepareBatch(ctx, "INSERT INTO test_json_table (raw)")
if err != nil {
log.Fatal(err)
}
for _, row := range rows {
if err := batch.Append(row); err != nil {
log.Fatal(err)
}
}
if err := batch.Send(); err != nil {
log.Fatal(err)
}
result, err := conn.Query(ctx, "SELECT raw FROM test_json_table SETTINGS max_memory_usage=0")
if err != nil {
log.Fatal(err)
}
defer result.Close()
var n int
for result.Next() {
var raw clickhouse.JSON
if err := result.Scan(&raw); err != nil {
log.Fatal(err)
}
n++
}
if err := result.Err(); err != nil {
log.Fatal(err)
}
fmt.Printf("read %d rows OK\n", n)
}
Run with:
docker run --rm -p 9002:9000 -p 8123:8123 \
-e CLICKHOUSE_PASSWORD=password \
clickhouse/clickhouse-server:26.2
go run .
Error log
read data: failed to decode block from 127.0.0.1:9002 (conn_id=1, compression=none): unexpected value 97 for boolean
In production with larger datasets the panic form is observed:
panic: runtime error: makeslice: len out of range
goroutine 120050 [running]:
github.com/ClickHouse/clickhouse-go/v2/lib/column.(*JSON).decodeObjectData_v1
lib/column/json_deprecated.go:111
github.com/ClickHouse/clickhouse-go/v2/lib/column.(*JSON).Decode
lib/column/json.go:768
github.com/ClickHouse/clickhouse-go/v2/lib/column.(*Dynamic).decodeData_v1
lib/column/dynamic_deprecated.go:174
github.com/ClickHouse/clickhouse-go/v2/lib/column.(*Dynamic).Decode
github.com/ClickHouse/clickhouse-go/v2/lib/column.(*Array).Decode
lib/column/array.go:227
github.com/ClickHouse/clickhouse-go/v2/lib/proto.(*Block).Decode
lib/proto/block.go:232
github.com/ClickHouse/clickhouse-go/v2.(*connect).readData
conn_process.go:107
Root cause (hypothesis)
ClickHouse 26.2 sends JSON column data in the deprecated v0 wire format
(output_format_native_use_flattened_dynamic_and_json_serialization=0 by default). In this format
each JSON path with multiple inferred types is encoded as a Dynamic column with N variant types.
Dynamic.decodeHeader_v1 reads the N type names from the wire, then inserts "SharedVariant" and
re-sorts the list alphabetically. This shifts the driver's discriminator→column index mapping
relative to what the server wrote. For example, if a path has variants ["Array(Int64)", "String"],
the server uses discriminator 1 = String, but the driver maps discriminator 1 → SharedVariant
(which sorts between "Array..." and "String..."). Every subsequent column read is then offset,
causing the decoder to consume the wrong bytes — producing a makeslice panic, an unexpected-boolean
error, or an unexpected-packet error depending on what those bytes look like to the next decoder.
Details
Environment
clickhouse-go version: v2.45.0
- Interface: ClickHouse API (
clickhouse.Open)
- Go version: 1.26.0
- Operating system: macOS / darwin arm64
- ClickHouse version: 26.2.15.4
- Is it a ClickHouse Cloud? No (also reproduces on self-hosted)
- ClickHouse Server non-default settings: none
(output_format_native_use_flattened_dynamic_and_json_serialization=0 is the default)
CREATE TABLE statement:
CREATE TABLE test_json_table (raw JSON(max_dynamic_paths=32))
ENGINE = MergeTree ORDER BY tuple()
Observed
JSONcolumn and insert 4 rows with heterogeneous value types across the same JSONpaths (e.g.
latency_msis an array in one row, a string in another).SELECTthe column back using the Native protocol withclickhouse-gov2.panic: runtime error: makeslice: len out of range(in production with large datasets)read data: failed to decode block: unexpected value N for booleanclickhouse [process]: unexpected packet NExpected behaviour
The
SELECTshould return all rows without error.Code example
Run with:
docker run --rm -p 9002:9000 -p 8123:8123 \ -e CLICKHOUSE_PASSWORD=password \ clickhouse/clickhouse-server:26.2 go run .Error log
In production with larger datasets the panic form is observed:
Root cause (hypothesis)
ClickHouse 26.2 sends JSON column data in the deprecated v0 wire format
(
output_format_native_use_flattened_dynamic_and_json_serialization=0by default). In this formateach JSON path with multiple inferred types is encoded as a Dynamic column with N variant types.
Dynamic.decodeHeader_v1reads the N type names from the wire, then inserts"SharedVariant"andre-sorts the list alphabetically. This shifts the driver's discriminator→column index mapping
relative to what the server wrote. For example, if a path has variants
["Array(Int64)", "String"],the server uses discriminator
1 = String, but the driver maps discriminator1 → SharedVariant(which sorts between
"Array..."and"String..."). Every subsequent column read is then offset,causing the decoder to consume the wrong bytes — producing a makeslice panic, an unexpected-boolean
error, or an unexpected-packet error depending on what those bytes look like to the next decoder.
Details
Environment
clickhouse-goversion: v2.45.0clickhouse.Open)(
output_format_native_use_flattened_dynamic_and_json_serialization=0is the default)CREATE TABLEstatement: