{"record":{"id":"341ba2f4c0ae4a2a","repo":"FlowiseAI/Flowise","slug":"invalid-sql-statement-statement-is-required-and-m","errorCode":null,"errorMessage":"Invalid SQL statement: statement is required and must be a string","messagePattern":"Invalid SQL statement: statement is required and must be a string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/src/validator.ts","lineNumber":381,"sourceCode":" * single read-only SELECT/WITH statement.\n *\n * The Sql Database Chain hands LLM-generated SQL directly to TypeORM's raw query\n * executor with no statement-type filtering. Without this guard, a compromised or\n * malicious LLM response can run `ATTACH DATABASE`/`VACUUM INTO`/bare `PRAGMA` to write\n * arbitrary files anywhere the process can write, bypassing validateSQLitePath (which\n * only constrains the initial connection path, not queries run afterward).\n *\n * All legitimate queries issued against sqlite by this chain (including langchain's own\n * schema introspection, which uses `pragma_table_info()` as a table-valued function\n * inside a SELECT) are single SELECT/WITH statements, so this restriction does not\n * affect normal operation.\n *\n * @param {string} sql The SQL statement to validate\n * @throws {Error} If the statement is not a single read-only SELECT/WITH statement\n */\nexport const assertReadOnlySqlStatement = (sql: string): void => {\n    if (!sql || typeof sql !== 'string') {\n        throw new Error('Invalid SQL statement: statement is required and must be a string')\n    }\n\n    let trimmed = sql.trim()\n    // Strip at most one trailing semicolon (+ trailing whitespace)\n    trimmed = trimmed.replace(/;\\s*$/, '')\n\n    if (trimmed.includes(';')) {\n        throw new Error('Invalid SQL statement: multiple statements are not allowed')\n    }\n\n    if (!/^(SELECT|WITH)\\b/i.test(trimmed)) {\n        throw new Error('Invalid SQL statement: only read-only SELECT/WITH statements are allowed')\n    }\n\n    if (/load_extension\\s*\\(/i.test(trimmed)) {\n        throw new Error('Invalid SQL statement: load_extension is not allowed')\n    }\n}","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/src/validator.ts#L363-L399","documentation":"Thrown by assertReadOnlySqlStatement (packages/components/src/validator.ts:381) when the SQL argument is falsy or not a string. The guard is applied to LLM-generated SQL in the Sql Database Chain so only read-only SELECT/WITH can run; a non-string input fails the type check before any SQL parsing.","triggerScenarios":"assertReadOnlySqlStatement is called with undefined, null, '', a number, or an object — e.g. the LLM returned no SQL, a tool returned a parsed object instead of text, or a caller forgot to extract the .sql field.","commonSituations":"LLM hallucinated an empty/structured response; chattool forwarded a JSON object instead of the SQL string; refactored code changed the value passed to the validator.","solutions":["Extract the SQL string from the LLM/tool response before calling the validator (e.g. response.text or response.sql).","Default to a safe no-op query like 'SELECT 1' when the LLM returns nothing.","Add a typeof sql === 'string' && sql.trim() guard upstream."],"exampleFix":"// before\nassertReadOnlySqlStatement(llmResponse)   // llmResponse is { sql: '...' }\n\n// after\nassertReadOnlySqlStatement(llmResponse?.sql ?? 'SELECT 1')","handlingStrategy":"type-guard","validationCode":"if (typeof sql !== 'string' || sql.trim() === '') throw new Error('SQL statement missing');\nassertReadOnlySqlStatement(sql);","typeGuard":"const isSqlString = (s: unknown): s is string => typeof s === 'string' && s.trim() !== '';","tryCatchPattern":"try { assertReadOnlySqlStatement(sql) } catch (e) { if (e instanceof Error && /statement is required/.test(e.message)) { sql = 'SELECT 1' } else throw e }","preventionTips":["Extract the .sql/.text field from LLM/tool responses before validation.","Default to 'SELECT 1' when the LLM returns nothing.","Type-narrow upstream with typeof sql === 'string'."],"tags":["sql","security","validation","type-guard","sqlite","database","llm","flowise"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}