dani-garcia/vaultwarden · error

Error loading push devices

Error message

Error loading push devices

What it means

Panic from `.expect()` on the Diesel query in `Device::find_push_devices_by_user`, which loads a user's devices that have a non-null push_token. Any SQL failure (connection loss, schema drift, bad query) is treated as fatal and panics. Absence of push devices is NOT this error — that just yields an empty vec.

Solutions

  1. Ensure the `devices.push_token` column exists (run all pending migrations by restarting the server)
  2. Check database availability and connection-pool settings (DB_TIMEOUT, max connections)
  3. Fix the root Diesel error shown in the log before the panic (connectivity, permissions, missing table)
  4. If push notifications were recently enabled, verify schema matches the new version before deploying

Example fix

// before
.load::<Self>(conn).expect("Error loading push devices")
// after
.load::<Self>(conn).map_err(|e| err!(DbError, "Error loading push devices: {e}"))
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the push_token column exists on old databases
// SELECT push_token FROM devices LIMIT 1;  (fails on pre-migration schema)

Try / catch

// Treat DB failure as service degradation: return empty push list or 503, not a crash
let devices = find_push_devices().unwrap_or_default();

Prevention

When it happens

Trigger: Querying push devices for a user during a DB outage; after a migration that altered `devices` (e.g. missing `push_token` column on an older DB); stale pooled connections after a database restart.

Common situations: Older database upgraded to a Vaultwarden version requiring the `push_token` column; MySQL/Postgres max_connections exhausted under load; read-replica lag or failover mid-request.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


AI-assisted analysis of dani-garcia/vaultwarden@cc67d644f6 (2026-09-19). Data as JSON: /api/errors/f2477b8ab8885c7b. Report an issue: GitHub.

Appendix: source

Thrown at src/db/models/device.rs:247

    pub async fn find_latest_active_by_user(user_uuid: &UserId, conn: &DbConn) -> Option<Self> {
        conn.run(move |conn| {
            devices::table
                .filter(devices::user_uuid.eq(user_uuid))
                .order(devices::updated_at.desc())
                .first::<Self>(conn)
                .ok()
        })
        .await
    }

    pub async fn find_push_devices_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
        conn.run(move |conn| {
            devices::table
                .filter(devices::user_uuid.eq(user_uuid))
                .filter(devices::push_token.is_not_null())
                .load::<Self>(conn)
                .expect("Error loading push devices")
        })
        .await
    }

    pub async fn check_user_has_push_device(user_uuid: &UserId, conn: &DbConn) -> bool {
        conn.run(move |conn| {
            devices::table
                .filter(devices::user_uuid.eq(user_uuid))
                .filter(devices::push_token.is_not_null())
                .count()
                .first::<i64>(conn)
                .ok()
                .unwrap_or(0)
                != 0
        })
        .await
    }

View on GitHub (pinned to cc67d644f6)