{"record":{"id":"3f9ccb43063b720e","repo":"can1357/oh-my-pi","slug":"sqlite-table-table-does-not-expose-rowid-use","errorCode":null,"errorMessage":"SQLite table '${table}' does not expose ROWID; use '?where=' instead","messagePattern":"SQLite table '(.+?)' does not expose ROWID; use '\\?where=' instead","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/sqlite-reader.ts","lineNumber":715,"sourceCode":"\t}\n\n\tconst column = primaryKeyColumns[0]!;\n\treturn { column: column.name, type: column.type };\n}\n\nexport function resolveTableRowLookup(db: Database, table: string): SqliteRowLookup {\n\tconst primaryKeyColumns = getPrimaryKeyColumns(db, table);\n\tif (primaryKeyColumns.length === 1) {\n\t\tconst column = primaryKeyColumns[0]!;\n\t\treturn { kind: \"pk\", column: column.name, type: column.type };\n\t}\n\tif (primaryKeyColumns.length > 1) {\n\t\tthrow new ToolError(`SQLite table '${table}' has a composite primary key; use '?where=' instead`);\n\t}\n\n\tconst schema = getTableSchema(db, table);\n\tif (/\\bWITHOUT\\s+ROWID\\b/i.test(schema)) {\n\t\tthrow new ToolError(`SQLite table '${table}' does not expose ROWID; use '?where=' instead`);\n\t}\n\n\treturn { kind: \"rowid\" };\n}\n\nexport function queryRows(\n\tdb: Database,\n\ttable: string,\n\topts: { limit: number; offset: number; order?: string; where?: string },\n): { columns: string[]; rows: Record<string, unknown>[]; totalCount: number } {\n\tconst columns = getTableColumns(db, table);\n\tconst validatedWhere = validateWhereClause(opts.where);\n\tconst whereClause = validatedWhere ? ` WHERE ${validatedWhere}` : \"\";\n\tconst orderClause = resolveOrderClause(opts.order, columns);\n\tconst countSql = `SELECT COUNT(*) AS count FROM ${quoteSqliteIdentifier(table)}${whereClause}`;\n\tconst selectSql = `SELECT * FROM ${quoteSqliteIdentifier(table)}${whereClause}${orderClause} LIMIT ? OFFSET ?`;\n\tconst totalCount = db.prepare<SqliteCountRow, []>(countSql).get()?.count ?? 0;\n\tconst statement = db.prepare<SqliteRow, SQLQueryBindings[]>(selectSql);","sourceCodeStart":697,"sourceCodeEnd":733,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/sqlite-reader.ts#L697-L733","documentation":"When a table has no declared primary key, row lookup falls back to the implicit rowid. Tables declared `WITHOUT ROWID` have no rowid at all, so single-value addressing is impossible and the tool throws, directing callers to `?where=`.","triggerScenarios":"Calling resolveTableRowLookup on a table created with `CREATE TABLE t (...) WITHOUT ROWID` that also lacks a single-column PRIMARY KEY (note: most WITHOUT ROWID tables DO have a PRIMARY KEY and would hit the composite branch instead; this fires for keyless WITHOUT ROWID tables).","commonSituations":"Key-value stores created as `CREATE TABLE kv(k TEXT PRIMARY KEY, v) WITHOUT ROWID` with a composite or unusual key setup; hand-rolled schemas copying the WITHOUT ROWID optimization without a usable single key.","solutions":["Look the row up by its actual column(s): `db.sqlite:kv?where=k='foo'`","Use a raw query: `db.sqlite?q=SELECT * FROM kv WHERE k='foo'`","Recreate the table as a normal rowid table with a single-column primary key if row addressing is required"],"exampleFix":"// before\nsqlite://app.db?settings:some_key\n// after\nsqlite://app.db?settings?where=key='some_key'","handlingStrategy":"validation","validationCode":"const schemaRow = db.query(\"SELECT sql FROM sqlite_master WHERE name = ?\").get(table);\nif (schemaRow?.sql && /\\bWITHOUT\\s+ROWID\\b/i.test(schemaRow.sql)) {\n  // no rowid: use ?where= or q= instead of table:key\n}","typeGuard":null,"tryCatchPattern":"try {\n  const lookup = resolveTableRowLookup(db, table);\n} catch (err) {\n  if (err instanceof ToolError && err.message.includes(\"does not expose ROWID\")) {\n    // use where= selector on the table's real columns\n  } else throw err;\n}","preventionTips":["Know your schema: WITHOUT ROWID tables are never rowid-addressable","Use the real key columns in ?where= for WITHOUT ROWID tables","Avoid declaring WITHOUT ROWID tables without a single-column PRIMARY KEY"],"tags":["sqlite","rowid","schema"],"backgroundTag":"without-rowid-table","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}