{"record":{"id":"4749fddeafb86301","repo":"multica-ai/multica","slug":"record-migration-q-w","errorCode":null,"errorMessage":"record migration %q: %w","messagePattern":"record migration %q: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"server/cmd/migrate/main.go","lineNumber":450,"sourceCode":"\t\t// same version retries cleanly on the next invocation.\n\t\tif hook, ok := opts.Hooks[version]; ok && hook != nil {\n\t\t\tslog.Info(\"running pre-migration hook\", \"version\", version, \"direction\", opts.Direction)\n\t\t\tif err := hook(ctx, pool); err != nil {\n\t\t\t\treturn fmt.Errorf(\"pre-migration hook for %q (%s): %w\", version, opts.Direction, err)\n\t\t\t}\n\t\t}\n\n\t\tif _, err := conn.Exec(ctx, string(sql)); err != nil {\n\t\t\treturn fmt.Errorf(\"apply migration %q: %w\", file, err)\n\t\t}\n\n\t\tif opts.Direction == \"up\" {\n\t\t\t_, err = conn.Exec(ctx, insertSQL, version)\n\t\t} else {\n\t\t\t_, err = conn.Exec(ctx, deleteSQL, version)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"record migration %q: %w\", version, err)\n\t\t}\n\n\t\tfmt.Printf(\"  %s  %s\\n\", opts.Direction, version)\n\t}\n\n\treturn nil\n}\n\n// quoteQualifiedIdentifier safely quotes either an unqualified table\n// name (\"foo\") or a schema-qualified name (\"schema.foo\") for embedding\n// into a SQL statement. Postgres does not let parametrized queries\n// supply identifiers, so we have to interpolate, but pgx.Identifier\n// does the right escaping (double-quotes, embedded-quote handling).\n//\n// The accepted shape is exactly one or two dot-separated components.\n// Names containing more than one dot are rejected outright rather than\n// silently sanitized into a \"schema\".\"b.c\" reference, which is valid\n// SQL but almost certainly not what the caller meant.","sourceCodeStart":432,"sourceCodeEnd":468,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/cmd/migrate/main.go#L432-L468","documentation":"Returned when updating the schema_migrations bookkeeping row fails after the migration SQL applied successfully: the INSERT (up) or DELETE (down) of the version could not be executed. The schema change is applied but not recorded, leaving the version in a half-recorded state that a re-run must reconcile.","triggerScenarios":"The insertSQL/deleteSQL statement against schema_migrations errors — the table is missing/corrupt, the pinned connection was killed mid-run, a unique violation on re-inserting an already-present version, or the transaction/permissions on schema_migrations are wrong.","commonSituations":"schema_migrations was dropped or recreated manually; a prior crash left the version row inserted while the operator assumed it was absent; a DBA revoked INSERT on the migrations table from the migrating role.","solutions":["Check the wrapped error: a unique violation on insert means the version row already exists — inspect schema_migrations and reconcile manually.","If schema_migrations is missing, recreate it and repopulate applied versions before re-running.","If the connection dropped, verify Postgres logs, then re-run migrate — up will re-apply SQL, so confirm the migration body is idempotent (IF NOT EXISTS) or manually mark the version.","Never delete version rows for migrations whose changes are live; only for verified rolled-back ones."],"exampleFix":"-- before: version applied but row missing, re-run would double-apply\n-- inspect, then record manually:\nSELECT * FROM schema_migrations ORDER BY version;\nINSERT INTO schema_migrations (version) VALUES ('20260815000000');\n\n-- after: migrate now skips the already-applied version","handlingStrategy":"validation","validationCode":"-- Pre-flight before migrating: confirm the bookkeeping table is sane.\nSELECT EXISTS (\n  SELECT 1 FROM information_schema.tables\n  WHERE table_name = 'schema_migrations'\n) AS has_table;\n-- If false, recreate it and backfill applied versions before running migrate.","typeGuard":null,"tryCatchPattern":"err := runMigrations(ctx, pool, opts)\nif err != nil {\n    var pgErr *pgconn.PgError\n    if errors.As(err, &pgErr) && pgErr.Code == \"23505\" {\n        // version row already inserted: schema is applied, bookkeeping is fine\n        log.Printf(\"version already recorded; treating as applied\")\n        return nil // only after verifying the schema change really is present\n    }\n    return err\n}","preventionTips":["Treat schema_migrations as the single source of truth; never manually insert/delete rows unless reconciling a verified half-applied state.","After any failed migrate run, inspect schema_migrations before re-running.","Ensure the migrating role owns schema_migrations."],"tags":["go","database","migration","postgres","bookkeeping"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}