{"record":{"id":"af8681788149d2f7","repo":"can1357/oh-my-pi","slug":"sqlite-raw-queries-do-not-support-bound-parameters","errorCode":null,"errorMessage":"SQLite raw queries do not support bound parameters","messagePattern":"SQLite raw queries do not support bound parameters","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/sqlite-reader.ts","lineNumber":769,"sourceCode":"\tconst binding = coerceLookupValue(key, pk.type ?? \"\");\n\treturn db.prepare<SqliteRow, SQLQueryBindings[]>(sql).get(binding);\n}\n\nexport function getRowByRowId(db: Database, table: string, key: string): Record<string, unknown> | null {\n\tgetTableMasterRow(db, table);\n\tconst binding = coerceIntegerKey(key, \"SQLite ROWID\");\n\treturn db\n\t\t.prepare<SqliteRow, SQLQueryBindings[]>(`SELECT * FROM ${quoteSqliteIdentifier(table)} WHERE rowid = ? LIMIT 1`)\n\t\t.get(binding);\n}\n\nexport function executeReadQuery(\n\tdb: Database,\n\tsql: string,\n): { columns: string[]; rows: Record<string, unknown>[]; truncated: boolean } {\n\tconst statement = db.prepare<SqliteRow, []>(sql);\n\tif (statement.paramsCount > 0) {\n\t\tthrow new ToolError(\"SQLite raw queries do not support bound parameters\");\n\t}\n\tconst columns = [...statement.columnNames];\n\tconst rows: SqliteRow[] = [];\n\tlet truncated = false;\n\tfor (const row of statement.iterate()) {\n\t\tif (rows.length >= MAX_RAW_QUERY_ROWS) {\n\t\t\ttruncated = true;\n\t\t\tbreak;\n\t\t}\n\t\trows.push(row);\n\t}\n\treturn { columns, rows, truncated };\n}\n\nexport function insertRow(db: Database, table: string, data: Record<string, unknown>): void {\n\tgetTableMasterRow(db, table);\n\tconst entries = validateWriteColumns(db, table, data);\n\tif (entries.length === 0) {","sourceCodeStart":751,"sourceCodeEnd":787,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/sqlite-reader.ts#L751-L787","documentation":"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.","triggerScenarios":"Running `db.sqlite?q=SELECT * FROM t WHERE id = ?` (or `?1`, `:name`, `@name` placeholders) through the sqlite reader selector.","commonSituations":"Copying a prepared statement from application code into the q= selector; assuming the tool supports parameterized queries; templating tools that emit `?` placeholders.","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"],"exampleFix":"// before\nsqlite://app.db?q=SELECT * FROM users WHERE id = ?\n// after\nsqlite://app.db?q=SELECT * FROM users WHERE id = 42","handlingStrategy":"validation","validationCode":"// before building the q= selector, inline all values\nif (/\\?|:[a-zA-Z_]+|@[a-zA-Z_]+|\\$[a-zA-Z_]+/.test(sql)) {\n  throw new Error(\"Raw q= queries cannot contain bind placeholders; inline literal values\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  const result = executeReadQuery(db, sql);\n} catch (err) {\n  if (err instanceof ToolError && err.message.includes(\"bound parameters\")) {\n    // inline the values into sql and retry\n  } else throw err;\n}","preventionTips":["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"],"tags":["sqlite","parameter-binding","raw-sql"],"backgroundTag":"bound-parameters-not-supported","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}