{"record":{"id":"c7ba109df12143a6","repo":"Mintplex-Labs/anything-llm","slug":"failed-to-revoke-api-key","errorCode":null,"errorMessage":"Failed to revoke API key","messagePattern":"Failed to revoke API key","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"server/endpoints/browserExtension.js","lineNumber":218,"sourceCode":"        const { id } = request.params;\n        const user = await userFromSession(request, response);\n\n        if (multiUserMode(response) && user.role !== ROLES.admin) {\n          const apiKey = await BrowserExtensionApiKey.get({\n            id: parseInt(id),\n            user_id: user?.id,\n          });\n          if (!apiKey) {\n            return response.status(403).json({ error: \"Unauthorized\" });\n          }\n        }\n\n        const { success, error } = await BrowserExtensionApiKey.delete(id);\n        if (!success) throw new Error(error);\n        response.status(200).json({ success: true });\n      } catch (error) {\n        console.error(error);\n        response.status(500).json({ error: \"Failed to revoke API key\" });\n      }\n    }\n  );\n}\n\nmodule.exports = { browserExtensionEndpoints };\n","sourceCodeStart":200,"sourceCodeEnd":225,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/3aec848f2885144aa8f1e53b9731a04310d5d558/server/endpoints/browserExtension.js#L200-L225","documentation":"Catch-all 500 for DELETE /browser-extension/api-keys/:id. It fires when BrowserExtensionApiKey.delete(id) reports failure (success:false, error re-thrown). Notably the raw string id is passed to delete() unparsed, so a non-numeric :id reaches the SQL layer and can fail binding in better-sqlite3 - unlike the 403 branch, which does parseInt. Otherwise it is a DB-level delete failure (locked DB, missing row constraint).","triggerScenarios":"DELETE /browser-extension/api-keys/abc - the string 'abc' is bound where an integer id is expected; SQLITE_BUSY while another write holds the DB; schema drift after a partial migration.","commonSituations":"Hand-crafted URLs or scripts using the key string instead of id; two tabs revoking the same key at once.","solutions":["Read server logs - the re-thrown error is the delete() failure reason.","Use the numeric id returned by GET /browser-extension/api-keys in the URL.","Retry once after a few seconds if the log shows 'database is locked'."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (!/^\\d+$/.test(String(id))) throw new Error(`Invalid key id: ${id} - use the numeric id from the list endpoint`);","typeGuard":"const isNumericId = (v: unknown): v is string => typeof v === 'string' && /^\\d+$/.test(v);","tryCatchPattern":"try { await revokeKey(id); }\ncatch (e) {\n  if (e.status === 500 && /locked/i.test(String(e.body?.error))) { await sleep(1000); return revokeKey(id); }\n  throw e;\n}","preventionTips":["Always take the id from GET /browser-extension/api-keys.","Avoid two clients revoking the same key simultaneously."],"tags":["api-keys","http-500","sqlite","browser-extension"],"backgroundTag":"database-delete-failed","analyzedSha":"3aec848f2885144aa8f1e53b9731a04310d5d558","analyzedAt":"2026-08-18T10:02:21.017Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}