mastra-ai/mastra · error
Invalid : . Must start with a letter or underscore, contain…
Error message
Invalid ${kind}: ${name}. Must start with a letter or underscore, contain only letters, numbers, or underscores, and be at most 63 characters long. What it means
`parseSqlIdentifier` validates SQL table/column/index names against `SQL_IDENTIFIER_PATTERN` (start with letter or underscore, only letters/numbers/underscores) and a 63-character limit (PostgreSQL identifier max). It throws this error to prevent SQL injection and to surface names that would be truncated or require quoting by the storage layer.
Solutions
- Rename the identifier to match `[A-Za-z_][A-Za-z0-9_]{0,62}` — replace dashes/dots/spaces with underscores.
- Truncate or hash long names to stay under 63 characters (e.g. prefix + short hash).
- Sanitize dynamic names before calling the API: `name.replace(/[^A-Za-z0-9_]/g, '_')` and assert length.
- Use Mastra's configuration options (e.g. table name prefixes) that produce compliant names instead of raw user input.
Example fix
// before
const table = `mastra-${tenantSlug}_traces`; // dashes from slug
// after
const table = `mastra_${tenantSlug.replace(/[^A-Za-z0-9_]/g, '_')}_traces`; Defensive patterns
Strategy: validation
Validate before calling
const SQL_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
function assertSqlIdentifier(name: string, kind = 'identifier') {
if (!SQL_IDENTIFIER_PATTERN.test(name) || name.length > 63)
throw new Error(`Invalid ${kind}: ${name}`);
} Type guard
function isSqlIdentifier(name: string): name is `${string}` {
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && name.length <= 63;
} Try / catch
let table: SqlIdentifier;
try {
table = parseSqlIdentifier(rawTableName, 'table');
} catch (e) {
throw new Error(`Configured table name "${rawTableName}" is invalid: ${(e as Error).message}`);
} Prevention
- Derive dynamic table names with `raw.replace(/[^A-Za-z0-9_]/g, '_')` and cap length.
- Avoid hyphens/dots/spaces in env-driven prefixes and tenant slugs used in table names.
- Keep generated index names under 63 chars — truncate long prefixes and append a short hash.
When it happens
Trigger: Passing a table name, column name, conflict column, or index name to a storage/DB adapter API (e.g. `parsedTableName`, `parsedColumn`, `parsedIndexName` callers) containing hyphens, dots, spaces, unicode, quotes, or a name longer than 63 chars; dynamic table names derived from user input or environment values like a prefixed `MASTRA_*` scope.
Common situations: Naming storage tables after tenant/product slugs with dashes (`my-app_agents`); auto-generating index names that exceed 63 chars due to long prefixes; interpolated table names from env vars containing invalid characters.
Related errors
- Field key cannot be empty
- Invalid Code Mode external identifier
- Invalid field key segment
- A custom capture schema requires an onExtracted hook that…
- A multi-line review comment requires both startLine and…
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/1d71bc1c8356a8cf.
Report an issue: GitHub.
Appendix: source
Thrown at packages/core/src/utils.ts:595
/**
* Parses and returns a valid SQL identifier (such as a table or column name).
* The identifier must:
* - Start with a letter (a-z, A-Z) or underscore (_)
* - Contain only letters, numbers, or underscores
* - Be at most 63 characters long
*
* @param name - The identifier string to parse.
* @param kind - Optional label for error messages (e.g., 'table name').
* @returns The validated identifier as a branded type.
* @throws {Error} If the identifier does not conform to SQL naming rules.
*
* @example
* const id = parseSqlIdentifier('my_table'); // Ok
* parseSqlIdentifier('123table'); // Throws error
*/
export function parseSqlIdentifier(name: string, kind = 'identifier'): SqlIdentifier {
if (!SQL_IDENTIFIER_PATTERN.test(name) || name.length > 63) {
throw new Error(
`Invalid ${kind}: ${name}. Must start with a letter or underscore, contain only letters, numbers, or underscores, and be at most 63 characters long.`,
);
}
return name as SqlIdentifier;
}
/**
* Parses and returns a valid dot-separated SQL field key (e.g., 'user.profile.name').
* Each segment must:
* - Start with a letter (a-z, A-Z) or underscore (_)
* - Contain only letters, numbers, or underscores
* - Be at most 63 characters long
*
* @param key - The dot-separated field key string to parse.
* @returns The validated field key as a branded type.
* @throws {Error} If any segment of the key is invalid.
*
* @exampleView on GitHub (pinned to 75dd419e61)