{"record":{"id":"415f4c796a6d6612","repo":"FlowiseAI/Flowise","slug":"invalid-table-name-415f4c","errorCode":null,"errorMessage":"Invalid table name","messagePattern":"Invalid table name","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/nodes/recordmanager/MySQLRecordManager/MySQLrecordManager.ts","lineNumber":190,"sourceCode":"    lc_namespace = ['langchain', 'recordmanagers', 'mysql']\n    config: MySQLRecordManagerOptions\n    tableName: string\n    namespace: string\n\n    constructor(namespace: string, config: MySQLRecordManagerOptions) {\n        const { tableName } = config\n        this.namespace = namespace\n        this.tableName = tableName || 'upsertion_records'\n        this.config = config\n    }\n\n    sanitizeTableName(tableName: string): string {\n        // Trim and normalize case, turn whitespace into underscores\n        tableName = tableName.trim().toLowerCase().replace(/\\s+/g, '_')\n\n        // Validate using a regex (alphanumeric and underscores only)\n        if (!/^[a-zA-Z0-9_]+$/.test(tableName)) {\n            throw new Error('Invalid table name')\n        }\n\n        return tableName\n    }\n\n    private async getDataSource(): Promise<DataSource> {\n        const { mysqlOptions } = this.config\n        if (!mysqlOptions) {\n            throw new Error('No datasource options provided')\n        }\n        // Prevent using default Postgres port, otherwise will throw uncaught error and crashing the app\n        if (mysqlOptions.port === 5432) {\n            throw new Error('Invalid port number')\n        }\n        const dataSource = new DataSource(mysqlOptions)\n        await dataSource.initialize()\n        return dataSource\n    }","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/nodes/recordmanager/MySQLRecordManager/MySQLrecordManager.ts#L172-L208","documentation":"Thrown by MySQLRecordManager.sanitizeTableName after the supplied table name is trimmed, lowercased, and whitespace-collapsed. The guard rejects any name whose characters fall outside [a-zA-Z0-9_], which protects the raw string interpolation used later in INSERT/CREATE TABLE SQL. It is a deliberately strict allow-list because the value is concatenated into a query template rather than parameterized.","triggerScenarios":"Passing a tableName (via config.tableName or nodeData input) containing hyphens, dots, spaces that survive normalization, quotes, unicode, or any non-ASCII punctuation. A schema-qualified name like 'mydb.records' or a name with a leading digit-only segment after another violation also triggers it.","commonSituations":"User enters 'my-table' or 'flowise.records' in the Record Manager node's tableName field; copy-pasting a table name with a trailing newline or hidden BOM; migrating from Postgres (where sanitizeRecordManagerTableName is more permissive) to MySQL and reusing the same name.","solutions":["Rename the table to use only letters, digits, and underscores (e.g. 'upsertion_records', 'my_table').","Strip any schema/database prefix before passing the value; the manager writes to a single database.","If you must keep a hyphenated name, pre-check with /^[a-zA-Z0-9_]+$/ and reject upstream in the Flowise UI rather than at runtime.","Verify no invisible characters remain: tableName.trim() already runs, so the failure is a genuine disallowed character."],"exampleFix":"// before\nconst tableName = 'flowise-records'\n// after\nconst tableName = 'flowise_records'","handlingStrategy":"validation","validationCode":"function isValidTableName(name: string): boolean {\n  return typeof name === 'string' && /^[a-zA-Z0-9_]+$/.test(name.trim().toLowerCase().replace(/\\s+/g, '_'))\n}\n// before calling init:\nif (!isValidTableName(userTableName)) {\n  throw new Error('Table name may only contain letters, digits, and underscores.')\n}","typeGuard":"function isSafeTableName(name: unknown): name is string {\n  return typeof name === 'string' && /^[a-zA-Z0-9_]+$/.test(name)\n}","tryCatchPattern":"// sanitizeTableName is synchronous and non-retryable; validate pre-flight instead.\n// If wrapping init: catch and surface to the user as a config error, do not retry.\ntry {\n  await recordManager.createSchema()\n} catch (e) {\n  if (e instanceof Error && e.message === 'Invalid table name') {\n    // surface to UI; do not retry with the same name\n  }\n  throw e\n}","preventionTips":["Constrain the tableName input in the UI to [a-zA-Z0-9_] with a regex mask.","Reject schema-qualified names (db.table) at the form level.","Add a unit test that runs example user inputs through the regex."],"tags":["mysql","record-manager","validation","sql-injection-guard","config"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}