Debug a query
Trace an unexpected result from your installed version through types, SQL, and execution.
Check the installed version
If an example's method is missing, start with npm ls quarry or pnpm why quarry.
These docs cover Quarry 0.10.0, including APIs absent from 0.9.1.
Check release guidance before working around a missing method.
Inspect the SQL and bound values
const compiled = query.toSQL();
console.log(compiled.query);
console.log(compiled.params);Check the table alias, predicate, grouping, ordering, and limits together. Parameters remain separate from the SQL text. Avoid logging sensitive parameter values in production logs.
You can run the compiled query directly through your existing client:
const result = await client.query({
query: compiled.query,
query_params: compiled.params,
format: "JSONEachRow",
});
const rows = await result.json();This checks the generated SQL independently of Quarry's execution helper. For a fair comparison, preserve the result settings required by your query, especially outer-join null behavior and JSON serialization. The runtime semantics guide explains those settings.
To investigate execution cost, send the same SQL and parameters through the
client with an appropriate ClickHouse EXPLAIN prefix. Quarry does not currently
provide an explain() helper. See the
ClickHouse EXPLAIN reference.
Match the symptom to the cause
| Symptom | Check |
|---|---|
| Optional filter has no effect | Assign the new builder returned by where(); builders are immutable |
| Column is rejected by TypeScript | Use the active alias and select the column from the subquery that exposes it |
| An aggregate alias is rejected in GROUP BY | Group by the underlying expression; use output aliases for HAVING or ORDER BY |
| Counts are strings | UInt64 uses strings in ClickHouse JSON output |
| Unmatched LEFT JOIN values are zero or empty strings | Ordinary joins use ClickHouse defaults; leftJoinNullable() in 0.10.0 opts into nulls |
| Only one result is needed | First-row helpers in 0.10.0 add LIMIT 1; on 0.9.1, call .limit(1) explicitly |
| A first-row query is still expensive | LIMIT restricts returned rows; sorting and aggregation can still process many input rows |
| An export uses too much memory | Consume stream() incrementally and await destination writes |
Make parameter types explicit
Plain schemas do not provide runtime metadata for every ClickHouse type. Use
param(...) when a value needs a specific placeholder type:
import { param } from "quarry";
const filtered = query.where("created_at", ">=", param("2026-08-01 00:00:00", "DateTime"));For null checks, use whereNull() or whereNotNull(). To bind a nullable value
explicitly, use param(null, "Nullable(String)"). Bare null predicates are rejected.
Report a reproducible problem
Include the Quarry version, a reduced schema, the builder chain, generated SQL and parameters, and the expected result. For a type error, include the TypeScript version and exact diagnostic. Remove private data before sharing.
Bring a query
that demonstrates the issue. Use toAST() only if you need to inspect the
builder's internal query representation as well.