can1357/oh-my-pi · error · ToolError
SQLite where clause changed the expected pagination paramete
Error message
SQLite where clause changed the expected pagination parameters; use q=SELECT ... for raw SQL
What it means
queryRows interpolates a pre-validated WHERE clause as raw SQL into a SELECT with exactly two bound parameters (LIMIT ? and OFFSET ?). After preparing, it asserts statement.paramsCount === 2; a mismatch means the where string introduced its own `?` placeholders, which the pagination bindings would incorrectly satisfy, so the tool refuses and suggests raw SQL.
Source
Thrown at packages/coding-agent/src/tools/sqlite-reader.ts:735
return { kind: "rowid" };
}
export function queryRows(
db: Database,
table: string,
opts: { limit: number; offset: number; order?: string; where?: string },
): { columns: string[]; rows: Record<string, unknown>[]; totalCount: number } {
const columns = getTableColumns(db, table);
const validatedWhere = validateWhereClause(opts.where);
const whereClause = validatedWhere ? ` WHERE ${validatedWhere}` : "";
const orderClause = resolveOrderClause(opts.order, columns);
const countSql = `SELECT COUNT(*) AS count FROM ${quoteSqliteIdentifier(table)}${whereClause}`;
const selectSql = `SELECT * FROM ${quoteSqliteIdentifier(table)}${whereClause}${orderClause} LIMIT ? OFFSET ?`;
const totalCount = db.prepare<SqliteCountRow, []>(countSql).get()?.count ?? 0;
const statement = db.prepare<SqliteRow, SQLQueryBindings[]>(selectSql);
if (statement.paramsCount !== 2) {
throw new ToolError(
"SQLite where clause changed the expected pagination parameters; use q=SELECT ... for raw SQL",
);
}
const rows = statement.all(opts.limit, opts.offset);
return { columns, rows, totalCount };
}
export function getRowByKey(
db: Database,
table: string,
pk: { column: string; type?: string },
key: string,
): Record<string, unknown> | null {
getTableMasterRow(db, table);
const sql = `SELECT * FROM ${quoteSqliteIdentifier(table)} WHERE ${quoteSqliteIdentifier(pk.column)} = ? LIMIT 1`;
const binding = coerceLookupValue(key, pk.type ?? "");
return db.prepare<SqliteRow, SQLQueryBindings[]>(sql).get(binding);
}View on GitHub (pinned to 9690622007)
Solutions
- Inline literal values in the where clause instead of `?` placeholders (e.g. `where=name='foo'`)
- Switch to a raw query with q= if you need bound parameters: `db.sqlite?q=SELECT * FROM t WHERE name = ?` is not supported — inline the value
- Keep the where clause to literal SQL comparisons; the where param is validated SQL text, not a binding API
Example fix
// before sqlite://app.db?users?where=age > ? // after sqlite://app.db?users?where=age > 18
Defensive patterns
Strategy: validation
Validate before calling
// before passing where to the selector, ensure it contains no bind placeholders
if (whereClause.includes("?")) {
throw new Error("where= must be literal SQL without ? placeholders; inline values instead");
} Try / catch
try {
const result = queryRows(db, table, opts);
} catch (err) {
if (err instanceof ToolError && err.message.includes("pagination parameters")) {
// rewrite where clause with literal values or switch to q= raw SQL
} else throw err;
} Prevention
- Never put ? placeholders in the where= parameter — inline literal values
- Quote string literals properly in where= (e.g. name='foo')
- Use q= raw SQL only with inlined values too; this tool does not bind parameters
When it happens
Trigger: Passing ?where= containing a question-mark placeholder, e.g. `?where=name = ?`, making the prepared statement expect 3 params while queryRows supplies 2.
Common situations: Developers writing parameterized-style conditions out of habit; pasting a prepared-statement fragment into the where param; encoding issues turning something into a literal `?`.
Related errors
- SQLite order column '${column}' not found in table schema
- ${violation}
- SQLite raw queries do not support bound parameters
- Failed to open auth database at '${dbPath}' after ${maxAttem
- Persistent credential block store ${store} is unavailable af
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/5bbb7d1f408100c5.
Report an issue: GitHub.