ruvnet/ruflo · error · Error
Invalid schema name: "${schema}". Must contain only letters,
Error message
Invalid schema name: "${schema}". Must contain only letters, digits, and underscores, and start with a letter or underscore. What it means
Thrown by validateSchemaName() when the schema fails VALID_PG_IDENTIFIER (`^[a-zA-Z_][a-zA-Z0-9_]*$`). This is a primary SQL-injection guard: schemas are interpolated into DDL/DML, so any character outside the identifier grammar (quotes, semicolons, dashes, dots) is refused rather than escaped.
Source
Thrown at v3/@claude-flow/cli/src/commands/ruvector/pg-utils.ts:28
* Allows only ASCII letters, digits, and underscores.
* Must start with a letter or underscore.
*/
const VALID_PG_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
/**
* Validate a PostgreSQL schema name.
* Throws if the name contains characters that could enable SQL injection.
* Safe names are returned as-is (no quoting needed since they match the identifier pattern).
*/
export function validateSchemaName(schema: string): string {
if (!schema || schema.length === 0) {
throw new Error('Schema name must not be empty');
}
if (schema.length > 63) {
throw new Error(`Schema name too long (${schema.length} chars, max 63): "${schema}"`);
}
if (!VALID_PG_IDENTIFIER.test(schema)) {
throw new Error(
`Invalid schema name: "${schema}". Must contain only letters, digits, and underscores, and start with a letter or underscore.`
);
}
return schema;
}
/**
* Validate a PostgreSQL timestamp string.
* Only allows ISO 8601 format to prevent SQL injection via timestamp fields.
*/
const VALID_TIMESTAMP = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})?$/;
export function validateTimestamp(value: string): string {
if (!VALID_TIMESTAMP.test(value)) {
throw new Error(`Invalid timestamp format: "${value}". Expected ISO 8601.`);
}
return value;
}View on GitHub (pinned to 6b01dc5a68)
Solutions
- Use only ASCII letters, digits, and underscores; start with a letter or underscore.
- Convert kebab-case to snake_case: `name.replace(/-/g,'_')`.
- Split qualified names and validate each segment separately rather than passing `a.b`.
Example fix
// before
validateSchemaName('my-org')
// after
validateSchemaName('my_org') Defensive patterns
Strategy: validation
Validate before calling
const VALID_PG_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
function toPgIdentifier(name: string): string {
const cleaned = name.replace(/[^a-zA-Z0-9_]/g, '_').replace(/^[0-9]+/, '_');
if (!VALID_PG_IDENTIFIER.test(cleaned)) {
throw new Error(`Invalid schema name: ${name}`);
}
return cleaned;
} Type guard
const isPgIdentifier = (v: unknown): v is string => typeof v === 'string' && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(v);
Try / catch
try {
validateSchemaName(schema);
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
if (msg.startsWith('Invalid schema name')) {
schema = schema.replace(/-/g, '_');
validateSchemaName(schema); // retry once, cleaned
} else throw e;
} Prevention
- Use snake_case for schema names; never kebab-case.
- Validate the identifier at config load, not at SQL time.
- Split qualified names and validate each segment.
When it happens
Trigger: Passing a schema containing dots (namespace.schema), dashes (kebab-case tenant), quotes, semicolons, spaces, or non-ASCII characters; or a schema starting with a digit.
Common situations: Tenant identifiers in kebab-case ('my-org'), fully-qualified names ('ruvector.public'), user input containing whitespace, or internationalized names with non-ASCII characters.
Related errors
- Schema name must not be empty
- Invalid timestamp format: "${value}". Expected ISO 8601.
- Invalid embedding value at index ${i}: expected finite numbe
- Schema name too long (${schema.length} chars, max 63): "${sc
- Validation failed: ${result.error}
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/dec5f9bd84e85a30.
Report an issue: GitHub.