v2rayA/v2rayA · error
subscription count mismatch: BoltDB=%d, SQLite=%d
Error message
subscription count mismatch: BoltDB=%d, SQLite=%d
What it means
This error is thrown during the one-time BoltDB→SQLite migration when a post-migration verification step compares the number of subscription entries serialized inside the BoltDB 'subscriptions' JSON blob against the row count of the new SQLite 'subscriptions' table. A mismatch means some (or all) subscription records were not carried over, or extra rows exist, so the migration aborts rather than silently lose data.
Source
Thrown at service/db/migrate.go:556
serversData := touchBkt.Get([]byte("servers"))
if serversData != nil {
boltServerCount := len(gjson.ParseBytes(serversData).Array())
var sqlServerCount int
sqldb.QueryRow("SELECT COUNT(*) FROM servers WHERE type = 'server'").Scan(&sqlServerCount)
if boltServerCount != sqlServerCount {
return fmt.Errorf("server count mismatch: BoltDB=%d, SQLite=%d", boltServerCount, sqlServerCount)
}
log.Info("Servers: %d entries verified", boltServerCount)
}
// Verify subscriptions
subsData := touchBkt.Get([]byte("subscriptions"))
if subsData != nil {
boltSubCount := len(gjson.ParseBytes(subsData).Array())
var sqlSubCount int
sqldb.QueryRow("SELECT COUNT(*) FROM subscriptions").Scan(&sqlSubCount)
if boltSubCount != sqlSubCount {
return fmt.Errorf("subscription count mismatch: BoltDB=%d, SQLite=%d", boltSubCount, sqlSubCount)
}
log.Info("Subscriptions: %d entries verified", boltSubCount)
}
}
return nil
})
if err != nil {
return err
}
log.Warn("Migration verification passed!")
return nil
}
View on GitHub (pinned to 71e5442fc5)
Solutions
- Inspect the SQLite table: SELECT COUNT(*) FROM subscriptions; and compare with the count of elements in the BoltDB 'subscriptions' JSON blob to see which side is stale.
- If a previous partial migration duplicated rows, start from a clean SQLite DB (delete/rename the SQLite database file) and re-run the migration from the original BoltDB file.
- If BoltDB is the stale side (extra/obsolete subscriptions), prune the BoltDB subscriptions blob or accept the current SQLite state and skip re-import by not pointing at the old BoltDB file.
- Check the migration/importer logs for per-subscription insert failures; fix the malformed subscription entries that were skipped, then re-migrate.
- Back up both database files before any of the above.
Example fix
// before: verifying against an already-populated SQLite DB
err := migrate.FromBoltDB(boltPath)
// after: re-run migration against a fresh SQLite DB
os.Rename("v2raya.db", "v2raya.db.bak") // move old SQLite DB aside
err := migrate.FromBoltDB(boltPath) // clean import, counts now match Defensive patterns
Strategy: validation
Validate before calling
// Before migrating, snapshot and compare expected counts
var sqlCount int
if err := sqldb.QueryRow("SELECT COUNT(*) FROM subscriptions").Scan(&sqlCount); err != nil {
return fmt.Errorf("count subscriptions: %w", err)
}
boltCount := len(gjson.GetBytes(subsBlob, "subscriptions").Array())
if boltCount != sqlCount {
return fmt.Errorf("pre-check mismatch: BoltDB=%d, SQLite=%d — restore a clean SQLite DB before migrating", boltCount, sqlCount)
} Type guard
func isCountMismatch(err error) bool {
return err != nil && strings.Contains(err.Error(), "count mismatch")
} Try / catch
// Go: detect the mismatch sentinel and offer recovery instead of crashing
if err := migrate.FromBoltDB(boltPath); err != nil {
if isCountMismatch(err) {
log.Warn("migration verification failed: %v — keeping backup at %s", err, backupPath)
os.Rename(backupPath, dbPath) // roll back to pre-migration DB
}
return err
} Prevention
- Always back up the BoltDB and SQLite files before running a migration.
- Never re-run migration against a SQLite DB that already contains data; start from an empty database.
- Run only one v2rayA instance per config directory during migration.
- Log per-subscription insert failures so skipped rows are caught before the count check.
- Automate the BoltDB-vs-SQLite count comparison in CI for migration code changes.
When it happens
Trigger: Running the migration on a database where the BoltDB subscriptions JSON array contains a different number of elements than the SQLite subscriptions table has rows — e.g. a previous partial/failed migration left rows inserted, subscription rows were manually deleted/added after migration, or the importer skipped subscriptions that failed to parse.
Common situations: Re-running a migration after an earlier interrupted run; v2rayA upgrade where an old BoltDB file is imported into a SQLite DB that already has subscription rows; corrupt or hand-edited subscription JSON in BoltDB; DB file copied/restored from backups of mixed versions.
Related errors
- bolt.db exists, migration required
- failed to migrate system bucket: %w
- failed to migrate touch bucket: %w
- failed to migrate outbounds bucket: %w
- migration verification failed: %w
AI-assisted analysis of v2rayA/v2rayA@71e5442fc5 (2026-09-05).
Data as JSON: /api/errors/6b4d3ca3cb8a01ea.
Report an issue: GitHub.