{"record":{"id":"dec5f9bd84e85a30","repo":"ruvnet/ruflo","slug":"invalid-schema-name-schema-must-contain-onl","errorCode":null,"errorMessage":"Invalid schema name: \"${schema}\". Must contain only letters, digits, and underscores, and start with a letter or underscore.","messagePattern":"Invalid schema name: \"(.+?)\"\\. Must contain only letters, digits, and underscores, and start with a letter or underscore\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/commands/ruvector/pg-utils.ts","lineNumber":28,"sourceCode":" * Allows only ASCII letters, digits, and underscores.\n * Must start with a letter or underscore.\n */\nconst VALID_PG_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\n/**\n * Validate a PostgreSQL schema name.\n * Throws if the name contains characters that could enable SQL injection.\n * Safe names are returned as-is (no quoting needed since they match the identifier pattern).\n */\nexport function validateSchemaName(schema: string): string {\n  if (!schema || schema.length === 0) {\n    throw new Error('Schema name must not be empty');\n  }\n  if (schema.length > 63) {\n    throw new Error(`Schema name too long (${schema.length} chars, max 63): \"${schema}\"`);\n  }\n  if (!VALID_PG_IDENTIFIER.test(schema)) {\n    throw new Error(\n      `Invalid schema name: \"${schema}\". Must contain only letters, digits, and underscores, and start with a letter or underscore.`\n    );\n  }\n  return schema;\n}\n\n/**\n * Validate a PostgreSQL timestamp string.\n * Only allows ISO 8601 format to prevent SQL injection via timestamp fields.\n */\nconst VALID_TIMESTAMP = /^\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:?\\d{2})?$/;\n\nexport function validateTimestamp(value: string): string {\n  if (!VALID_TIMESTAMP.test(value)) {\n    throw new Error(`Invalid timestamp format: \"${value}\". Expected ISO 8601.`);\n  }\n  return value;\n}","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/commands/ruvector/pg-utils.ts#L10-L46","documentation":"validateSchemaName() only accepts identifiers matching ^[a-zA-Z_][a-zA-Z0-9_]*$ — ASCII letters, digits, and underscores, starting with a letter or underscore — because names matching that pattern can be interpolated into SQL unquoted. Hyphens, spaces, unicode, or a leading digit throw this error.","triggerScenarios":"Passing kebab-case names (my-schema), names starting with a digit (2fa_data), or anything containing -, ., :, /, or non-ASCII characters as the schema.","commonSituations":"Translating npm-style kebab-case package or namespace names into schema names; templating schema names from URLs or file paths that contain dashes.","solutions":["Replace hyphens with underscores: my-schema -> my_schema","Prefix digit-leading names with an underscore or letter","Keep the schema to ASCII [A-Za-z0-9_] and have it start with a letter or underscore"],"exampleFix":"# before\nruflo ruvector ... --schema my-vector-schema\n\n# after\nruflo ruvector ... --schema my_vector_schema","handlingStrategy":"validation","validationCode":"function toPgIdentifier(raw: string): string {\n  const cleaned = raw.replace(/[^a-zA-Z0-9_]/g, '_');\n  return /^[a-zA-Z_]/.test(cleaned) ? cleaned : `_${cleaned}`;\n}\nconst schema = toPgIdentifier(userInput); // safe before it reaches the CLI","typeGuard":"function isPgIdentifier(v: unknown): v is string {\n  return typeof v === 'string' && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(v) && v.length <= 63;\n}","tryCatchPattern":null,"preventionTips":["Sanitize kebab-case inputs to snake_case before using them as schema names","Keep isPgIdentifier() next to wherever names enter your pipeline","Never template schema names from URLs or free-form text unchecked"],"tags":["database","postgresql","schema","sql-identifier","validation"],"backgroundTag":"invalid-sql-identifier","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}