{"record":{"id":"451c751fcc9a6420","repo":"dani-garcia/vaultwarden","slug":"error-creating-organization-api-key","errorCode":null,"errorMessage":"Error creating organization API Key","messagePattern":"Error creating organization API Key","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/api/core/organizations.rs","lineNumber":3224,"sourceCode":"        err!(\"Organization not found\", \"Organization id's do not match\");\n    }\n    let data: PasswordOrOtpData = data.into_inner();\n    let user = headers.user;\n\n    // Validate the admin users password/otp\n    data.validate(&user, true, &conn).await?;\n\n    let org_api_key = if let Some(mut org_api_key) = OrganizationApiKey::find_by_org_uuid(org_id, &conn).await {\n        if rotate {\n            org_api_key.api_key = crate::crypto::generate_api_key();\n            org_api_key.revision_date = chrono::Utc::now().naive_utc();\n            org_api_key.save(&conn).await.expect(\"Error rotating organization API Key\");\n        }\n        org_api_key\n    } else {\n        let api_key = crate::crypto::generate_api_key();\n        let new_org_api_key = OrganizationApiKey::new(org_id.clone(), api_key);\n        new_org_api_key.save(&conn).await.expect(\"Error creating organization API Key\");\n        new_org_api_key\n    };\n\n    Ok(Json(json!({\n      \"apiKey\": org_api_key.api_key,\n      \"revisionDate\": crate::util::format_date(&org_api_key.revision_date),\n      \"object\": \"apiKey\",\n    })))\n}\n\n#[post(\"/organizations/<org_id>/api-key\", data = \"<data>\")]\nasync fn post_api_key(\n    org_id: OrganizationId,\n    data: Json<PasswordOrOtpData>,\n    headers: AdminHeaders,\n    conn: DbConn,\n) -> JsonResult {\n    api_key(&org_id, data, false, headers, conn).await","sourceCodeStart":3206,"sourceCodeEnd":3242,"githubUrl":"https://github.com/dani-garcia/vaultwarden/blob/0cefa4cca7c9f2a5579dd290f78193b543818c51/src/api/core/organizations.rs#L3206-L3242","documentation":"A Rust .expect() panic raised when inserting a brand-new OrganizationApiKey row fails: the else-branch at organizations.rs:3221-3225 runs when no organization_api_key row exists yet for the org (first-time API key retrieval), generates a key, and .save() returns Err. Reached via POST /organizations/<org_id>/api-key (rotate=false) after admin auth and password/OTP validation succeed. The panic surfaces to the client as an HTTP 500 with no useful body.","triggerScenarios":"An org admin calls POST /organizations/<org_id>/api-key with valid credentials for an organization that has never generated an API key (find_by_org_uuid returns None), and the INSERT fails: missing organization_api_key table (migrations never run), unique-constraint violation on org_uuid from a concurrent first-time request racing to insert, DB connection loss, or a read-only/full disk.","commonSituations":"Upgraded instance where migrations were skipped so the table/columns do not exist; SQLite data directory mounted read-only or with wrong ownership in Docker; two clients requesting the key simultaneously on first generation; Postgres/MySQL out of disk or with revoked INSERT privileges; schema drift between the running binary and the database.","solutions":["Read the server log for the underlying diesel error — 'no such table organization_api_key' means migrations are missing; 'readonly database' means file/mount permissions; a constraint error means a race or duplicate row.","Run/verify database migrations for the deployed version so the organization_api_key table and its constraints exist and match the model.","Fix storage access: ensure the data directory and SQLite file are writable by the service user (chmod/chown, correct Docker volume mount), or confirm the external DB grants INSERT.","Convert the .expect() into error propagation so first-time key creation failures return a proper error response instead of a panic, and consider handling the unique-race by re-fetching the row on conflict."],"exampleFix":"// before\nlet new_org_api_key = OrganizationApiKey::new(org_id.clone(), api_key);\nnew_org_api_key.save(&conn).await.expect(\"Error creating organization API Key\");\nnew_org_api_key\n\n// after (propagate as an API error instead of panicking)\nlet new_org_api_key = OrganizationApiKey::new(org_id.clone(), api_key);\nnew_org_api_key.save(&conn).await.map_err(|e| {\n    error!(\"Error creating organization API key for {}: {:#?}\", org_id, e);\n    Error::new(\"Error creating organization API key\", \"Failed to save the new API key\")\n})?;\nnew_org_api_key","handlingStrategy":"validation","validationCode":"-- Pre-check that first-time key creation can succeed\n-- 1) table exists and is writable\nSELECT count(*) FROM organization_api_key;\n-- 2) no pre-existing row for this org (else the rotate path applies)\nSELECT * FROM organization_api_key WHERE org_uuid = '<org_id>';\n-- 3) (SQLite) file and directory writable by the service user\n--    ls -l <data_dir>/db.sqlite3 && test -w <data_dir>","typeGuard":null,"tryCatchPattern":"// Recommended: convert the panic into a returned error; on a unique-constraint\n// race (two first-time requests), re-fetch the existing row instead of failing.\nif let Err(e) = new_org_api_key.save(&conn).await {\n    if let Some(existing) = OrganizationApiKey::find_by_org_uuid(org_id, &conn).await {\n        return Ok(Json(json!({ \"apiKey\": existing.api_key, \"revisionDate\": crate::util::format_date(&existing.revision_date), \"object\": \"apiKey\" })));\n    }\n    return Err(Error::new(\"Error creating organization API key\", \"Database write failed\"));\n}","preventionTips":["Apply all migrations when upgrading so organization_api_key exists before any client requests an API key.","Ensure the service account has INSERT privilege on the table (or write access to the SQLite file and its directory).","Avoid concurrent first-time API-key requests from multiple clients — they race on the same insert.","Prefer explicit error propagation over .expect() in request handlers so failures return structured 500s and keep the worker alive."],"tags":["rust","panic","database","api-key","insert-failure","migrations"],"backgroundTag":null,"analyzedSha":"0cefa4cca7c9f2a5579dd290f78193b543818c51","analyzedAt":"2026-08-16T07:44:56.102Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}