v2rayA/v2rayA · error
ListSet: failed to clear old servers of subscription %d: %w
Error message
ListSet: failed to clear old servers of subscription %d: %w
What it means
ListSet wraps the error from DELETE FROM servers WHERE type='subscription_server'. This delete of the subscription's old servers runs after outbound_connections are cleared (which satisfied the FK); a failure here aborts ListSet before inserting the new server rows. The %w cause carries the real DB error.
Source
Thrown at service/db/listOp.go:79
if err != nil {
return err
}
rows, _ := result.RowsAffected()
if rows == 0 {
return fmt.Errorf("ListSet: subscription at index %d not found", index)
}
// Update servers within this subscription.
// Clean up outbound_connections first to satisfy foreign key constraint;
// otherwise the delete fails and the insert below duplicates the list.
if _, err := db.Exec(`
DELETE FROM outbound_connections
WHERE server_id IN (SELECT id FROM servers WHERE type = 'subscription_server' AND sub_id = ?)
`, subID); err != nil {
return fmt.Errorf("ListSet: failed to clear outbound connections of subscription %d: %w", index, err)
}
if _, err := db.Exec("DELETE FROM servers WHERE type = 'subscription_server' AND sub_id = ?", subID); err != nil {
return fmt.Errorf("ListSet: failed to clear old servers of subscription %d: %w", index, err)
}
servers := parsed.Get("servers").Array()
for j, s := range servers {
_, err := db.Exec(
"INSERT INTO servers (type, sub_id, config_json, sort) VALUES ('subscription_server', ?, ?, ?)",
subID, s.Raw, j,
)
if err != nil {
return fmt.Errorf("ListSet: failed to update subscription server %d/%d: %w", index, j, err)
}
}
return nil
default:
return fmt.Errorf("ListSet: unsupported bucket/key: %s/%s", bucket, key)
}
}View on GitHub (pinned to 71e5442fc5)
Solutions
- Read the wrapped cause to identify the driver-level failure
- Verify the servers table schema (columns type, sub_id, config_json, sort) exists
- Eliminate concurrent writers or use a proper transaction/locking mode
- Retry after DB access is restored
Example fix
// before
if _, err := db.Exec("DELETE FROM servers WHERE type = 'subscription_server' AND sub_id = ?", subID); err != nil { ... }
// after
// same call, but ensure single-writer access / WAL mode: PRAGMA journal_mode=WAL; then retry ListSet Defensive patterns
Strategy: try-catch
Validate before calling
var ok int
db.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='servers'").Scan(&ok)
if ok == 0 { return errors.New("servers table missing; run migrations") } Try / catch
err := ListSet("touch", "servers", val)
if err != nil {
if errors.Is(err, sql.ErrConnDone) || strings.Contains(err.Error(), "locked") { time.Sleep(retryDelay); retry() }
return err
} Prevention
- Enable journal_mode=WAL to reduce lock contention
- Serialize writes through one goroutine or a mutex
- Ping the DB before batch operations
- Verify servers table columns (type, sub_id, config_json, sort) exist
When it happens
Trigger: ListSet on touch/servers or touch/subscriptions where the DELETE FROM servers for the given subID fails: locked DB, missing servers table, connection error.
Common situations: Concurrent writers locking the SQLite file; migration never created the servers table; DB file corruption or read-only filesystem; expired/closed connection in db.Exec.
Related errors
- ListSet: failed to clear outbound connections of subscriptio
- ListSet: failed to update subscription server %d/%d: %w
- ListAppend: failed to insert subscription server: %w
- failed to begin transaction: %w
- bolt.db exists, migration required
AI-assisted analysis of v2rayA/v2rayA@71e5442fc5 (2026-09-05).
Data as JSON: /api/errors/fe4e2183c62e57b4.
Report an issue: GitHub.