{"record":{"id":"9a400ce585a7a595","repo":"v2rayA/v2rayA","slug":"failed-to-migrate-touch-bucket-w","errorCode":null,"errorMessage":"failed to migrate touch bucket: %w","messagePattern":"failed to migrate touch bucket: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"service/db/migrate.go","lineNumber":76,"sourceCode":"\tdefer func() {\n\t\tif p := recover(); p != nil {\n\t\t\t_ = tx.Rollback()\n\t\t\tpanic(p)\n\t\t}\n\t}()\n\n\t// Migrate system bucket\n\tif err := migrateSystemBucket(boltDB, tx); err != nil {\n\t\t_ = tx.Rollback()\n\t\treturn fmt.Errorf(\"failed to migrate system bucket: %w\", err)\n\t}\n\n\t// Migrate touch bucket (servers and subscriptions)\n\t// subIDMap maps BoltDB subscription index (0-based) -> SQLite subscription ID\n\tsubIDMap, err := migrateTouchBucket(boltDB, tx)\n\tif err != nil {\n\t\t_ = tx.Rollback()\n\t\treturn fmt.Errorf(\"failed to migrate touch bucket: %w\", err)\n\t}\n\n\t// NOTE: Accounts are NOT migrated. Users must re-register after migration.\n\t// This is intentional: the old MD5-based password hashing is deprecated,\n\t// and requiring re-registration ensures users set up fresh bcrypt-based credentials.\n\n\t// Migrate outbounds bucket\n\tif err := migrateOutboundsBucket(boltDB, tx, subIDMap); err != nil {\n\t\t_ = tx.Rollback()\n\t\treturn fmt.Errorf(\"failed to migrate outbounds bucket: %w\", err)\n\t}\n\n\t// Commit transaction\n\tif err := tx.Commit(); err != nil {\n\t\treturn fmt.Errorf(\"failed to commit migration transaction: %w\", err)\n\t}\n\n\t// Verify data integrity","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/v2rayA/v2rayA/blob/71e5442fc548c05680ee55eae943e6c0afe9ac51/service/db/migrate.go#L58-L94","documentation":"This error is returned by MigrateFromBoltDB when the 'touch' BoltDB bucket (servers and subscriptions) could not be migrated to the SQLite servers/subscriptions tables. It wraps the underlying cause: invalid or non-array JSON in the servers/subscriptions keys, an INSERT failure for a server or subscription, or a LastInsertId failure. The transaction is rolled back and no subscription/server data is written.","triggerScenarios":"migrateTouchBucket returns an error: the bbolt View fails, the 'servers' or 'subscriptions' values are not valid JSON or not JSON arrays (gjson.ValidBytes/IsArray checks in migrateServers/migrateSubscriptions), an INSERT into servers/subscriptions fails, or res.LastInsertId() fails while building subIDMap.","commonSituations":"Upgrading v2rayA with a bolt.db whose touch bucket was corrupted by an earlier crash or by editing the file externally; JSON payloads that fail validation; SQLite constraint/schema problems on first run after upgrade; disk-full during the write-heavy server/subscription import.","solutions":["Read the wrapped cause: 'invalid JSON for servers', 'servers data is not an array', 'failed to insert server N', etc., to pinpoint the failing record.","Validate the JSON stored in the touch bucket (export it with bbolt tooling); repair or hand-fix malformed records in bolt.db or accept data loss for the bad record.","Remove the leftover v2raya.db and restart so migration retries cleanly after fixing the source data.","Check disk space and SQLite errors (disk I/O, constraint violations) in the wrapped message.","Restore bolt.db from a backup if the touch bucket is corrupt."],"exampleFix":"// before: bolt.db 'subscriptions' value is corrupt JSON\n// error: failed to migrate touch bucket: invalid JSON for subscriptions\n\n// after: restore a known-good bolt.db before restarting\nmv /etc/v2raya/bolt.db /etc/v2raya/bolt.db.corrupt\ncp /backup/bolt.db /etc/v2raya/bolt.db  # then restart the service","handlingStrategy":"try-catch","validationCode":"// Go: pre-check the touch bucket JSON before migrating (read-only bbolt open)\nboltDB.View(func(tx *bbolt.Tx) error {\n    bkt := tx.Bucket([]byte(\"touch\")); if bkt == nil { return nil }\n    for _, k := range []string{\"servers\", \"subscriptions\"} {\n        if v := bkt.Get([]byte(k)); v != nil && !gjson.ValidBytes(v) {\n            return fmt.Errorf(\"touch/%s holds invalid JSON; repair bolt.db first\", k)\n        }\n    }\n    return nil\n})","typeGuard":null,"tryCatchPattern":"if err := db.MigrateFromBoltDB(); err != nil {\n    if strings.Contains(err.Error(), \"failed to migrate touch bucket\") {\n        // restore a known-good bolt.db, remove the partial sqlite db, retry\n        log.Fatalf(\"touch bucket migration failed: %v\", err)\n    }\n    return err\n}","preventionTips":["Back up bolt.db before upgrading.","Do not kill the process mid-write; corrupt JSON in touch bucket is the main trigger.","Validate bolt.db contents with bbolt tooling after an unclean shutdown.","Ensure ample disk space for the server/subscription import.","Remove the leftover v2raya.db after any failed migration attempt before restarting."],"tags":["database","migration","boltdb","sqlite","json"],"backgroundTag":"database-migration-failed","analyzedSha":"71e5442fc548c05680ee55eae943e6c0afe9ac51","analyzedAt":"2026-09-05T20:04:37.459Z","contentChangedAt":"2026-09-05T20:04:37.459Z","schemaVersion":2},"datasetVersion":"2026-09-15T02:17:10.978Z"}