{"record":{"id":"b147731aa0c72cd7","repo":"can1357/oh-my-pi","slug":"sqlite-order-direction-must-be-asc-or-desc-go","errorCode":null,"errorMessage":"SQLite order direction must be 'asc' or 'desc'; got '${direction}'","messagePattern":"SQLite order direction must be 'asc' or 'desc'; got '(.+?)'","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/sqlite-reader.ts","lineNumber":390,"sourceCode":"function resolveOrderClause(order: string | undefined, columns: string[]): string {\n\tif (!order) return \"\";\n\tconst trimmed = order.trim();\n\tif (!trimmed) return \"\";\n\n\tconst separatorIndex = trimmed.lastIndexOf(\":\");\n\tconst column = separatorIndex === -1 ? trimmed : trimmed.slice(0, separatorIndex);\n\tconst direction =\n\t\tseparatorIndex === -1\n\t\t\t? \"asc\"\n\t\t\t: trimmed\n\t\t\t\t\t.slice(separatorIndex + 1)\n\t\t\t\t\t.trim()\n\t\t\t\t\t.toLowerCase();\n\tif (!columns.includes(column)) {\n\t\tthrow new ToolError(`SQLite order column '${column}' not found in table schema`);\n\t}\n\tif (direction !== \"asc\" && direction !== \"desc\") {\n\t\tthrow new ToolError(`SQLite order direction must be 'asc' or 'desc'; got '${direction}'`);\n\t}\n\treturn ` ORDER BY ${quoteSqliteIdentifier(column)} ${direction.toUpperCase()}`;\n}\n\nconst FORBIDDEN_WHERE_KEYWORDS = new Set([\n\t\"limit\",\n\t\"offset\",\n\t\"union\",\n\t\"intersect\",\n\t\"except\",\n\t\"attach\",\n\t\"detach\",\n\t\"pragma\",\n]);\n\nconst COMMENT_OR_TERMINATOR_ERROR =\n\t\"SQLite 'where' clause must not contain comments or statement terminators; use '?q=SELECT ...' for raw SQL\";\nconst FORBIDDEN_KEYWORD_ERROR =","sourceCodeStart":372,"sourceCodeEnd":408,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/sqlite-reader.ts#L372-L408","documentation":"Thrown by resolveOrderClause when the direction suffix of the 'order' parameter is not exactly 'asc' or 'desc' (after trimming and lowercasing). The tool whitelists directions so only validated, safe ORDER BY clauses are built.","triggerScenarios":"Passing order like 'name:ASCENDING', 'name:1', 'name:up', or 'name:' with a junk/empty direction suffix after the ':' separator.","commonSituations":"Copy-pasting SQL-style direction keywords ('ASCENDING', 'ascending', 'ascending order'); using numeric sort direction codes; locale issues where a direction word got translated.","solutions":["Use 'asc' or 'desc' as the direction suffix","Omit the direction entirely (defaults to 'asc')","Check the separator format: order is 'column[:direction]'","Strip whitespace — trailing text after asc/desc still fails"],"exampleFix":"// before\n?order=created_at:ascending\n// after\n?order=created_at:desc","handlingStrategy":"validation","validationCode":"const dir = (orderDirection ?? 'asc').trim().toLowerCase();\nif (dir !== 'asc' && dir !== 'desc') {\n  throw new Error(`direction must be asc|desc, got '${dir}'`);\n}","typeGuard":"function isSortDirection(v: string): v is 'asc' | 'desc' {\n  return v === 'asc' || v === 'desc';\n}","tryCatchPattern":"try {\n  await reader.read(`db.sqlite/users?order=name:${dir}`);\n} catch (err) {\n  if (err instanceof ToolError && err.message.includes(\"must be 'asc' or 'desc'\")) {\n    // retry with default direction\n  } else throw err;\n}","preventionTips":["Normalize direction input with trim().toLowerCase() before building the URL","Constrain direction to a union type 'asc'|'desc' in your own code","Omit the direction suffix when you want ascending"],"tags":["sqlite","validation","parameter-format"],"backgroundTag":"invalid-parameter-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}