{"record":{"id":"7d0d1b8d01df5e7d","repo":"RocketChat/Rocket.Chat","slug":"error-application-not-found","errorCode":"error-application-not-found","errorMessage":"Application not found","messagePattern":"Application not found","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/auth/deleteOAuthApp.ts","lineNumber":23,"sourceCode":"\nimport { hasPermissionAsync } from '../../lib/authorization/hasPermission';\nimport { methodDeprecationLogger } from '../../lib/deprecationWarningLogger';\n\ndeclare module '@rocket.chat/ddp-client' {\n\t// eslint-disable-next-line @typescript-eslint/naming-convention\n\tinterface ServerMethods {\n\t\tdeleteOAuthApp(applicationId: IOAuthApps['_id']): boolean;\n\t}\n}\n\nexport const deleteOAuthApp = async (userId: string, applicationId: IOAuthApps['_id']): Promise<boolean> => {\n\tif (!(await hasPermissionAsync(userId, 'manage-oauth-apps'))) {\n\t\tthrow new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'deleteOAuthApp' });\n\t}\n\n\tconst application = await OAuthApps.findOneAndDeleteById(applicationId, { projection: { clientId: 1 } });\n\tif (!application) {\n\t\tthrow new Meteor.Error('error-application-not-found', 'Application not found', {\n\t\t\tmethod: 'deleteOAuthApp',\n\t\t});\n\t}\n\n\tawait OAuthAccessTokens.deleteMany({ clientId: application.clientId });\n\tawait OAuthAuthCodes.deleteMany({ clientId: application.clientId });\n\n\treturn true;\n};\n\nMeteor.methods<ServerMethods>({\n\tasync deleteOAuthApp(applicationId) {\n\t\tmethodDeprecationLogger.method('deleteOAuthApp', '9.0.0', '/v1/oauth-apps.delete');\n\t\tif (!this.userId) {\n\t\t\tthrow new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'deleteOAuthApp' });\n\t\t}\n\n\t\treturn deleteOAuthApp(this.userId, applicationId);","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/auth/deleteOAuthApp.ts#L5-L41","documentation":"deleteOAuthApp calls OAuthApps.findOneAndDeleteById(applicationId) with a clientId projection; when no OAuth app document matches, it throws 'error-application-not-found'. Lookup and delete are atomic, so a concurrent deletion by another admin also surfaces as this error.","triggerScenarios":"Deleting with a stale or wrong _id; the app was already removed by another tab/admin; the id string is malformed (not a valid document _id).","commonSituations":"Double-click submitting the delete twice; the UI list is stale because someone else deleted the app; an id copied incompletely out of a URL.","solutions":["Re-fetch the current app list (GET /api/v1/oauth-apps.list) and use the returned _id","Treat 'error-application-not-found' as success in idempotent delete flows — the app is already gone","Validate the id is a well-formed document _id before calling"],"exampleFix":"// before — stale id from an old page load\nawait Meteor.callAsync('deleteOAuthApp', staleAppId);\n\n// after — resolve the id from the live list\nconst { oauthApps } = await fetch('/api/v1/oauth-apps.list', { headers }).then((r) => r.json());\nconst app = oauthApps.find((a) => a.name === 'My App');\nif (app) await Meteor.callAsync('deleteOAuthApp', app._id);","handlingStrategy":"validation","validationCode":"// confirm the app exists with the exact id before deleting\nconst { oauthApps } = await fetch('/api/v1/oauth-apps.list', { headers }).then((r) => r.json());\nconst exists = oauthApps.some((a) => a._id === applicationId);\nif (!exists) {\n  // already deleted: treat as success, skip the call\n}","typeGuard":"const isMeteorErrorCode = (e: unknown, code: string): e is Meteor.Error => e instanceof Meteor.Error && e.error === code;","tryCatchPattern":"try {\n  await Meteor.callAsync('deleteOAuthApp', applicationId);\n} catch (err) {\n  if (isMeteorErrorCode(err, 'error-application-not-found')) {\n    // idempotent delete: the app is gone — swallow and report success\n  }\n}","preventionTips":["Always resolve ids from a fresh list call at action time instead of cached page state","Design delete flows to treat 'not found' as success","Disable the button after first submission to avoid double-delete races"],"tags":["oauth","not-found","meteor-methods","idempotency"],"backgroundTag":"resource-not-found","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}