{"record":{"id":"7b4423ac98ca8b6d","repo":"can1357/oh-my-pi","slug":"label-must-be-an-integer-got-key","errorCode":null,"errorMessage":"${label} must be an integer; got '${key}'","messagePattern":"(.+?) must be an integer; got '(.+?)'","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/sqlite-reader.ts","lineNumber":348,"sourceCode":"function getTableInfoRows(db: Database, table: string): SqliteTableInfoRow[] {\n\tgetTableMasterRow(db, table);\n\treturn db.prepare<SqliteTableInfoRow, []>(`PRAGMA table_info(${quoteSqliteIdentifier(table)})`).all();\n}\n\nfunction getTableColumns(db: Database, table: string): string[] {\n\treturn getTableInfoRows(db, table).map(column => column.name);\n}\n\nfunction getPrimaryKeyColumns(db: Database, table: string): SqliteTableInfoRow[] {\n\treturn getTableInfoRows(db, table)\n\t\t.filter(column => column.pk > 0)\n\t\t.sort((left, right) => left.pk - right.pk);\n}\n\nfunction coerceIntegerKey(key: string, label: string): number | bigint {\n\tconst trimmed = key.trim();\n\tif (!/^-?\\d+$/.test(trimmed)) {\n\t\tthrow new ToolError(`${label} must be an integer; got '${key}'`);\n\t}\n\n\tconst asNumber = Number.parseInt(trimmed, 10);\n\tif (Number.isSafeInteger(asNumber)) {\n\t\treturn asNumber;\n\t}\n\treturn BigInt(trimmed);\n}\n\nfunction coerceLookupValue(key: string, type: string): SqliteBinding {\n\tconst normalizedType = type.trim().toUpperCase();\n\tif (normalizedType.includes(\"INT\")) {\n\t\treturn coerceIntegerKey(key, `Primary key '${key}'`);\n\t}\n\tif (normalizedType.includes(\"REAL\") || normalizedType.includes(\"FLOA\") || normalizedType.includes(\"DOUB\")) {\n\t\tconst parsed = Number(key);\n\t\tif (Number.isFinite(parsed)) {\n\t\t\treturn parsed;","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/sqlite-reader.ts#L330-L366","documentation":"coerceIntegerKey converts a string key into a number/bigint for binding against an INTEGER primary key or rowid. If the key string is not a plain (optionally signed) decimal integer, it throws this ToolError with the given label (e.g. \"Primary key 'abc'\"). Very large integers fall back to BigInt, so only non-integer shapes fail.","triggerScenarios":"Row lookup/keyed update with a non-integer key against an INT-typed primary key: UUID-style keys ('a1b2...'), composite keys ('1,2'), keys with whitespace-embedded junk, prefixed ids ('id:42'), or float strings ('1.0'). Called from coerceLookupValue, binding, and updateRowByRowId.","commonSituations":"Using a text primary key (UUID) against a table whose declared column type contains INT (note: SQLite type affinity matches on 'INT' substring); passing composite key parts joined into one string; copying a rowid that includes formatting.","solutions":["Pass a plain integer string (e.g. '42', '-7') for INT primary-key lookups, or use the rowid if the table lacks a suitable key.","If the key is genuinely non-integer (UUID), the table's declared type likely shouldn't match INT — check PRAGMA table_info; the tool coerces based on declared type, so look up by the correct column.","For composite primary keys, use the appropriate keyed-update path with each column bound separately rather than one joined string.","Trim the key and validate with /^-?\\d+$/ before calling."],"exampleFix":"// before\nawait reader.row({ table: \"users\", key: \"7f3c-uuid\" }); // pk column is INTEGER\n// after\nawait reader.row({ table: \"users\", key: \"17\" }); // use the integer pk/rowid","handlingStrategy":"validation","validationCode":"function assertIntegerKey(key: string): number {\n  const t = key.trim();\n  if (!/^-?\\d+$/.test(t)) throw new Error(`key must be an integer: ${key}`);\n  return Number.parseInt(t, 10);\n}","typeGuard":"function isIntegerKey(v: string): boolean { return /^-?\\d+$/.test(v.trim()); }","tryCatchPattern":"try { return await reader.row({ table, key }); } catch (e) { if (e instanceof ToolError && e.message.includes(\"must be an integer\")) { /* look up by rowid or re-fetch the correct pk */ } throw e; }","preventionTips":["Match key type to the declared column type — the tool coerces via PRAGMA type substring 'INT'.","For UUID/text keys, ensure the table's pk column is actually TEXT-typed.","Strip formatting (whitespace, prefixes) from ids before lookup."],"tags":["validation","sqlite","parameters"],"backgroundTag":"invalid-key-type","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}