dani-garcia/vaultwarden · error
Error saving API key
Error message
Error saving API key
What it means
update_api_key backs POST /accounts/api-key and POST /accounts/rotate-api-key. After PasswordOrOtpData validation succeeds, a new API key is generated and the user row saved with user.save(&conn).await.expect("Error saving API key") — a database failure at this point panics the handler instead of returning a proper error response, so the client typically sees a 500/connection reset, and the key was generated in memory but not persisted.
Source
Thrown at src/api/core/accounts.rs:1389
if !user.check_valid_password(&data.master_password_hash) {
err!("Invalid password")
}
kdf_upgrade(&mut user, &data.master_password_hash, &conn).await?;
Ok(Json(master_password_policy(&user, &conn).await))
}
async fn update_api_key(data: Json<PasswordOrOtpData>, rotate: bool, headers: Headers, conn: DbConn) -> JsonResult {
let data: PasswordOrOtpData = data.into_inner();
let mut user = headers.user;
data.validate(&user, true, &conn).await?;
if rotate || user.api_key.is_none() {
user.api_key = Some(crypto::generate_api_key());
user.save(&conn).await.expect("Error saving API key");
}
Ok(Json(json!({
"apiKey": user.api_key,
"revisionDate": format_date(&user.updated_at),
"object": "apiKey",
})))
}
#[post("/accounts/api-key", data = "<data>")]
async fn post_api_key(data: Json<PasswordOrOtpData>, headers: Headers, conn: DbConn) -> JsonResult {
update_api_key(data, false, headers, conn).await
}
#[post("/accounts/rotate-api-key", data = "<data>")]
async fn rotate_api_key(data: Json<PasswordOrOtpData>, headers: Headers, conn: DbConn) -> JsonResult {
update_api_key(data, true, headers, conn).await
}View on GitHub (pinned to 0cefa4cca7)
Solutions
- Check DB health and disk space, then retry the request
- For SQLite: enable WAL and ensure only one instance writes the file
- Run pending migrations so the schema matches the binary
- Code fix: propagate the save error with ? instead of expect
Example fix
// before
user.save(&conn).await.expect("Error saving API key");
// after
user.save(&conn).await.map_err(|e| Error::new("Failed to save API key", e.to_string()))?; Defensive patterns
Strategy: try-catch
Validate before calling
# Pre-flight the DB before rotating keys (sqlite example) sqlite3 /data/db.sqlite3 'PRAGMA quick_check;' && echo db-ok
Try / catch
// Replace panic-prone expects with propagated errors in handlers
match user.save(&conn).await {
Ok(_) => {}
Err(e) => {
error!("Failed to save API key for user {}: {e}", user.uuid);
return Err(Error::new("Failed to save API key", e.to_string()));
}
} Prevention
- Monitor DB connectivity and disk space
- Enable WAL for SQLite and keep write workloads single-instance
- Run migrations after upgrades before serving traffic
- Avoid rotating API keys during maintenance windows
When it happens
Trigger: Calling POST /api/accounts/api-key or /api/accounts/rotate-api-key when the user-row update fails: lost DB connection, SQLite 'database is locked' under concurrency, disk full, or missing migrations.
Common situations: SQLite deployments with concurrent writers and no WAL; DB restarted mid-request; disk pressure; the user row concurrently modified by another session (e.g. sync).
Related errors
- Error rotating organization API Key
- Error creating organization API Key
- Error saving attachment
- Error updating attachment
- Grantee user should exist but does not!
AI-assisted analysis of dani-garcia/vaultwarden@0cefa4cca7 (2026-08-16).
Data as JSON: /api/errors/76d302446224539c.
Report an issue: GitHub.