{"record":{"id":"72fbe80cbe5a3b93","repo":"multica-ai/multica","slug":"invalid-schema-migrations-table-q-w","errorCode":null,"errorMessage":"invalid schema migrations table %q: %w","messagePattern":"invalid schema migrations table %q: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/cmd/migrate/main.go","lineNumber":349,"sourceCode":"// processes against the same database with the same options: every\n// caller blocks on pg_advisory_lock, and once it is their turn the\n// already-applied EXISTS check turns each finished migration into a\n// no-op skip. See GitHub multica-ai/multica#3647 / MUL-2923.\nfunc runMigrations(ctx context.Context, pool *pgxpool.Pool, opts runOptions) error {\n\tswitch opts.Direction {\n\tcase \"up\", \"down\":\n\t\t// ok\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid direction %q (want \\\"up\\\" or \\\"down\\\")\", opts.Direction)\n\t}\n\n\ttable := opts.SchemaMigrationsTable\n\tif table == \"\" {\n\t\ttable = defaultSchemaMigrationsTable\n\t}\n\ttableIdent, err := quoteQualifiedIdentifier(table)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid schema migrations table %q: %w\", table, err)\n\t}\n\tlockKey := opts.AdvisoryLockKey\n\tif lockKey == 0 {\n\t\tlockKey = migrationAdvisoryLockKey\n\t}\n\n\t// pg_advisory_lock is scoped to a single session, so we must pin one\n\t// *pgxpool.Conn for the whole run — calling pool.Exec would attach the\n\t// lock to a random connection that pgxpool could hand back out before\n\t// the loop finishes, making the lock effectively a no-op. We use the\n\t// blocking pg_advisory_lock (not pg_try_*) so a late-arriving runner\n\t// queues behind the current one instead of crash-looping; once it\n\t// acquires the lock the EXISTS checks below turn finished migrations\n\t// into no-op skips.\n\t//\n\t// We deliberately do NOT wrap the loop in a single transaction: the\n\t// repo already ships migrations using CREATE INDEX CONCURRENTLY,\n\t// which Postgres rejects inside a transaction block.","sourceCodeStart":331,"sourceCodeEnd":367,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/cmd/migrate/main.go#L331-L367","documentation":"quoteQualifiedIdentifier rejected the schema_migrations table name before any SQL ran. The table identifier is interpolated into CREATE TABLE / EXISTS / INSERT / DELETE statements, so a name that cannot be safely quoted (empty components, NUL bytes, or an invalid qualified form) is refused rather than risk malformed or injectable SQL.","triggerScenarios":"Passing -schema-migrations-table=\"\" explicitly (the empty default is replaced, but an explicitly empty qualified form like \"public.\" is not), a name with embedded NUL, or a malformed multi-part identifier.","commonSituations":"Custom tracking-table names in multi-tenant setups; script variables that expand to a partially-set value.","solutions":["Use a valid identifier: single name (schema_migrations) or schema-qualified (myschema.schema_migrations)","Check the variable/flag expansion for empties or trailing dots","Avoid NUL/control characters in the configured name"],"exampleFix":"# before\nmigrate -schema-migrations-table=\"tenant.\"\n\n# after\nmigrate -schema-migrations-table=\"tenant.schema_migrations\"","handlingStrategy":"validation","validationCode":"if strings.Contains(name, \"\\x00\") || strings.HasPrefix(name, \".\") || strings.HasSuffix(name, \".\") || name == \"\" {\n    return fmt.Errorf(\"bad table name %q\", name)\n}","typeGuard":null,"tryCatchPattern":"tableIdent, err := quoteQualifiedIdentifier(table)\nif err != nil {\n    return fmt.Errorf(\"invalid schema migrations table %q: %w\", table, err)\n}","preventionTips":["Use simple identifiers (letters, digits, underscore) or valid schema.name forms","Assert flag variables are non-empty before running migrations"],"tags":["go","postgres","validation","identifier","migration"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}