Go and TypeScript can load the wrong ClickHouse line. A request for a LOWER line is answered by a HIGHER line already in the cache. Measured on published 1.0.2.
What happens. Each case below used a fresh cache. "Returned" is the loaded library's own reported ClickHouse version.
| binding |
cache holds |
request |
returned |
| Go 1.0.2 |
26.8.15.10 |
For("26.3") |
26.8.15.10 |
| Go 1.0.2 |
26.8.15.10 |
For("26.3.38.2") (exact) |
26.8.15.10 |
| Go 1.0.2 |
26.9.8.3 |
For("26.8") |
26.9.8.3 |
| TS 1.0.2 |
the same three |
the same three |
the same wrong answers |
| Go/TS 1.0.2 |
26.3.38.2 |
For("26.8") |
26.8.15.10 (correct) |
| Go/TS 1.0.2 |
empty |
For("26.3") |
26.3.38.2 (correct) |
| Python 1.0.2 |
26.8.15.10 |
26.3 / 26.3.38.2 |
26.3.38.2 (correct) |
- Rust is correct by its code (
inferred; not run here).
- The writer doesn't matter. A cache written by Go, Python or TypeScript is answered wrongly by every Go and TypeScript reader.
- The CLI does the same.
chtypes fetch 26.8 --cache C, then chtypes fetch 26.3 --cache C, exits 0, prints the 26.8 directory plus a "keeping the existing/newer install" warning, and installs nothing for 26.3. A later chtypes fetch 26.3 --offline (Go) reports CHTYPES_ARTIFACT_MISSING. The online and offline paths disagree, so the cache never repairs itself.
Cause. The "never go backwards" (monotonic) check compares the resolved candidate against EVERY installed entry for the platform, ignoring the request:
- Go:
newerAlreadyInstalled, go/internal/ocifetch/ensure.go;
- TypeScript:
checkMonotonic, ts/src/ocifetch/ensure.ts.
Rust (ensure.rs) considers only installed builds within the version request (the check is skipped only for a literal, non-numeric tag request). Python (_ensure.py) compares only within the same ClickHouse version.
This violates docs/guides/fetch-v1.md §9: a request may be answered only by a build whose signed version is within it.
Who is affected. Anyone using Go or TypeScript whose cache holds more than one ClickHouse line: a shared or mounted cache with autofetch, CI that tests several lines, or a developer machine. The wrong library loads silently, apart from the warning, and every answer it gives is the other line's.
Workaround until the fix ships:
- keep one ClickHouse line per cache directory;
- after opening, assert the loaded library's version is within the requested line (Go:
strings.HasPrefix(lib.Version(), "26.3.")), and refuse otherwise.
Fix (planned as 1.0.4, all four bindings together):
- Go and TypeScript: the monotonic check considers only installs within the request (same line; an exact request is never overridden), matching Rust and Python.
- A load-time assertion in all four bindings: the loaded library's reported version must be within the requested line, else a typed error. A wrong-line load then fails loudly, whatever the cache did.
- Regression: a fetch conformance case (cache holds a higher line, request a lower one and an exact lower build) run by every binding, plus an N×N case across writer binding × reader binding × line in the cache-interop job.
🤖 Generated with Claude Code
Go and TypeScript can load the wrong ClickHouse line. A request for a LOWER line is answered by a HIGHER line already in the cache. Measured on published 1.0.2.
What happens. Each case below used a fresh cache. "Returned" is the loaded library's own reported ClickHouse version.
For("26.3")For("26.3.38.2")(exact)For("26.8")For("26.8")For("26.3")26.3/26.3.38.2inferred; not run here).chtypes fetch 26.8 --cache C, thenchtypes fetch 26.3 --cache C, exits 0, prints the 26.8 directory plus a "keeping the existing/newer install" warning, and installs nothing for 26.3. A laterchtypes fetch 26.3 --offline(Go) reportsCHTYPES_ARTIFACT_MISSING. The online and offline paths disagree, so the cache never repairs itself.Cause. The "never go backwards" (monotonic) check compares the resolved candidate against EVERY installed entry for the platform, ignoring the request:
newerAlreadyInstalled,go/internal/ocifetch/ensure.go;checkMonotonic,ts/src/ocifetch/ensure.ts.Rust (
ensure.rs) considers only installed builds within the version request (the check is skipped only for a literal, non-numeric tag request). Python (_ensure.py) compares only within the same ClickHouse version.This violates
docs/guides/fetch-v1.md§9: a request may be answered only by a build whose signed version is within it.Who is affected. Anyone using Go or TypeScript whose cache holds more than one ClickHouse line: a shared or mounted cache with autofetch, CI that tests several lines, or a developer machine. The wrong library loads silently, apart from the warning, and every answer it gives is the other line's.
Workaround until the fix ships:
strings.HasPrefix(lib.Version(), "26.3.")), and refuse otherwise.Fix (planned as 1.0.4, all four bindings together):
🤖 Generated with Claude Code