{"record":{"id":"a4cefbcebc4cd958","repo":"MHSanaei/3x-ui","slug":"copy-t-w","errorCode":null,"errorMessage":"copy %T: %w","messagePattern":"copy %T: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/database/migrate_data.go","lineNumber":130,"sourceCode":"\t\t// client_traffics rows whose inbound was deleted. Drop it here too so copying\n\t\t// such orphaned rows can't fail with an fk_inbounds_client_stats violation.\n\t\tif err := tx.Exec(\"ALTER TABLE client_traffics DROP CONSTRAINT IF EXISTS fk_inbounds_client_stats\").Error; err != nil {\n\t\t\treturn fmt.Errorf(\"drop legacy foreign key: %w\", err)\n\t\t}\n\n\t\t// Empty the destination tables before copying: a fresh PostgreSQL DB\n\t\t// already holds an auto-seeded admin (id=1) from any prior panel start,\n\t\t// so a plain INSERT with explicit ids would collide on users_pkey. Only\n\t\t// the panel's own tables are cleared, and a failure anywhere in this\n\t\t// transaction rolls the clear back with everything else.\n\t\tif err := truncatePostgresTables(tx, migrationModels()); err != nil {\n\t\t\treturn fmt.Errorf(\"clear destination tables: %w\", err)\n\t\t}\n\n\t\tfor _, m := range migrationModels() {\n\t\t\tn, err := copyTable(src, tx, m)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"copy %T: %w\", m, err)\n\t\t\t}\n\t\t\ttotalRows += n\n\t\t\tlog.Printf(\"  %-32s %d rows\", reflect.TypeOf(m).Elem().Name(), n)\n\t\t}\n\t\treturn nil\n\t})\n\tif txErr != nil {\n\t\treturn txErr\n\t}\n\n\t// setval is never rolled back by PostgreSQL, so sequences are resynced only\n\t// after the transaction has committed.\n\tif err := resetPostgresSequences(dst); err != nil {\n\t\tlog.Printf(\"warning: failed to reset some postgres sequences: %v\", err)\n\t}\n\n\tlog.Printf(\"Migration complete: %d rows across %d tables.\", totalRows, len(migrationModels()))\n\tlog.Println(\"Set XUI_DB_TYPE=postgres and XUI_DB_DSN=... in /etc/default/x-ui, then restart x-ui.\")","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/database/migrate_data.go#L112-L148","documentation":"Thrown when copyTable fails for one of the migrationModels() while inserting into PostgreSQL inside the transaction (find reads run on src, CreateInBatches writes on tx). copyTable streams source rows in 500-row batches and inserts them with explicit ids, so this wraps whichever batch hit the first constraint, type, or connection error. The failure aborts and rolls back the entire copy, including the earlier table clears.","triggerScenarios":"A destination FK referencing a panel table from OUTSIDE migrationModels() rejecting copied rows; a NOT NULL/CHECK/unique constraint on dst stricter than the SQLite data; a value SQLite stored loosely (e.g. oversized text, invalid UTF-8) rejected by Postgres; statement/network failure mid-copy; a serial sequence clash on unique columns other than id.","commonSituations":"Migrating a DB whose schema was hand-modified or extended with extra FKs; source backups from very old panel versions holding rows that violate newer constraints; copying millions of rows and hitting an idle/statement timeout on a managed Postgres.","solutions":["Unwrap the %w chain — the model %T and the Postgres SQLSTATE identify the offending table and constraint.","Inspect the failing table's rows on the SQLite source for NULLs in now-required columns or duplicate values on unique indexes; fix or drop them at the source and re-run (the migration is idempotent: it re-clears and re-copies).","Drop user-added FK/CHECK constraints on the destination that reference panel tables but are not part of the panel schema.","For timeouts, raise statement_timeout/lock_timeout for the migration session or batch-clean the source."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := RunSQLiteToPostgres(srcPath, dstDSN)\nif err != nil {\n    var pk *pgconn.PgError\n    if errors.As(err, &pk) && pk.Code == pgerrcode.ForeignKeyViolation {\n        // a non-panel FK references a migrated table: drop it on dst and re-run\n    }\n    // any failure rolled back the whole tx — destination unchanged, safe to re-run\n}","preventionTips":["Do not add external foreign keys or CHECK constraints that reference panel tables.","Clean obviously bad source rows (NULL ids, duplicate emails) before migrating.","Rely on the transaction: retry after fixing the named table, never hand-repair a half-copy."],"tags":["database","postgres","sqlite","migration","constraints","gorm"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}