can1357/oh-my-pi · error · ToolError
SQLite raw queries do not support bound parameters
Error message
SQLite raw queries do not support bound parameters
What it means
executeReadQuery prepares a raw `q=` SQL query typed with zero bound parameters and rejects any statement whose paramsCount > 0. The selector API is purely read-only and string-based (a URL query param), so it cannot accept separate binding values; instead of misbinding or enabling injection games, it throws.
Source
Thrown at packages/coding-agent/src/tools/sqlite-reader.ts:769
const binding = coerceLookupValue(key, pk.type ?? "");
return db.prepare<SqliteRow, SQLQueryBindings[]>(sql).get(binding);
}
export function getRowByRowId(db: Database, table: string, key: string): Record<string, unknown> | null {
getTableMasterRow(db, table);
const binding = coerceIntegerKey(key, "SQLite ROWID");
return db
.prepare<SqliteRow, SQLQueryBindings[]>(`SELECT * FROM ${quoteSqliteIdentifier(table)} WHERE rowid = ? LIMIT 1`)
.get(binding);
}
export function executeReadQuery(
db: Database,
sql: string,
): { columns: string[]; rows: Record<string, unknown>[]; truncated: boolean } {
const statement = db.prepare<SqliteRow, []>(sql);
if (statement.paramsCount > 0) {
throw new ToolError("SQLite raw queries do not support bound parameters");
}
const columns = [...statement.columnNames];
const rows: SqliteRow[] = [];
let truncated = false;
for (const row of statement.iterate()) {
if (rows.length >= MAX_RAW_QUERY_ROWS) {
truncated = true;
break;
}
rows.push(row);
}
return { columns, rows, truncated };
}
export function insertRow(db: Database, table: string, data: Record<string, unknown>): void {
getTableMasterRow(db, table);
const entries = validateWriteColumns(db, table, data);
if (entries.length === 0) {View on GitHub (pinned to 9690622007)
Solutions
- Inline the literal values directly into the SQL: `q=SELECT * FROM t WHERE id = 42`
- Ensure string literals are properly quoted: `q=SELECT * FROM t WHERE name = 'foo'`
- If parameterization is required, execute the query outside this tool with a real SQLite client (bun:sqlite) and bind values there
Example fix
// before sqlite://app.db?q=SELECT * FROM users WHERE id = ? // after sqlite://app.db?q=SELECT * FROM users WHERE id = 42
Defensive patterns
Strategy: validation
Validate before calling
// before building the q= selector, inline all values
if (/\?|:[a-zA-Z_]+|@[a-zA-Z_]+|\$[a-zA-Z_]+/.test(sql)) {
throw new Error("Raw q= queries cannot contain bind placeholders; inline literal values");
} Try / catch
try {
const result = executeReadQuery(db, sql);
} catch (err) {
if (err instanceof ToolError && err.message.includes("bound parameters")) {
// inline the values into sql and retry
} else throw err;
} Prevention
- Always inline literal values into q= queries (properly quoted for strings)
- Don't copy prepared statements from app code into q= selectors unchanged
- Use a direct SQLite client with .bind() if you genuinely need parameterization
When it happens
Trigger: Running `db.sqlite?q=SELECT * FROM t WHERE id = ?` (or `?1`, `:name`, `@name` placeholders) through the sqlite reader selector.
Common situations: Copying a prepared statement from application code into the q= selector; assuming the tool supports parameterized queries; templating tools that emit `?` placeholders.
Related errors
- SQLite where clause changed the expected pagination paramete
- Failed to open auth database at '${dbPath}' after ${maxAttem
- Persistent credential block store ${store} is unavailable af
- WAL checkpoint failed for ${dbPath}: busy=${result.busy}, wa
- Unsupported SQLite selector
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/af8681788149d2f7.
Report an issue: GitHub.