{"record":{"id":"ab0f9b8622557374","repo":"Budibase/budibase","slug":"oauth2-config-with-id-configid-not-found","errorCode":null,"errorMessage":"OAuth2 config with id '${configId}' not found.","messagePattern":"OAuth2 config with id '(.+?)' not found\\.","errorType":"http","errorClass":"HTTPError","httpStatus":404,"severity":"error","filePath":"packages/server/src/sdk/workspace/oauth2/crud.ts","lineNumber":98,"sourceCode":"      config.clientSecret === PASSWORD_REPLACEMENT\n        ? existing.clientSecret\n        : config.clientSecret,\n  }\n\n  const result = await db.put(toUpdate)\n\n  await cleanCache(config._id)\n\n  return { ...toUpdate, _rev: result.rev }\n}\n\nexport async function remove(configId: string, _rev: string): Promise<void> {\n  const db = context.getWorkspaceDB()\n  try {\n    await db.remove(configId, _rev)\n  } catch (e: any) {\n    if (e.status === 404) {\n      throw new HTTPError(`OAuth2 config with id '${configId}' not found.`, 404)\n    }\n    throw e\n  }\n\n  await cleanCache(configId)\n\n  const usageLog = await db.tryGet(docIds.generateOAuth2LogID(configId))\n  if (usageLog) {\n    await db.remove(usageLog)\n  }\n}\n\nasync function cleanCache(configId: string) {\n  await cache.destroy(cache.CacheKey.OAUTH2_TOKEN(configId))\n}\n","sourceCodeStart":80,"sourceCodeEnd":114,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/oauth2/crud.ts#L80-L114","documentation":"remove(configId, _rev) deletes the OAuth2 config document directly from the workspace DB. CouchDB/PouchDB throws a status-404 conflict when the document (id+rev) does not exist, and the catch block converts that into this 404 HTTPError so callers get a consistent error. Any other DB error is rethrown unchanged.","triggerScenarios":"Calling remove() with a configId that was already deleted, an id that never existed, an outdated _rev (document changed/deleted since read), or an id from a different workspace DB.","commonSituations":"Double-clicking a delete button so the second request hits an already-deleted doc; stale revision after concurrent edit; idempotent retry logic re-running a delete.","solutions":["Check the config still exists (fetch()/get()) before removing, or treat 404 as success in idempotent flows","Re-fetch the config to obtain a fresh _rev and retry the remove","Confirm the id belongs to the current workspace","Clean up stale UI state that re-issues deletes"],"exampleFix":"// before\nawait sdk.workspace.oauth2.remove(config._id, config._rev)\n// after\ntry {\n  await sdk.workspace.oauth2.remove(config._id, config._rev)\n} catch (e) {\n  if (e.status !== 404) throw e // already deleted — treat as success\n}","handlingStrategy":"try-catch","validationCode":"const existing = await sdk.workspace.oauth2.get(configId)\nif (!existing) return // already deleted; nothing to do","typeGuard":"function isNotFound(e: unknown): e is HTTPError {\n  return e instanceof HTTPError && e.status === 404\n}","tryCatchPattern":"try {\n  await sdk.workspace.oauth2.remove(configId, rev)\n} catch (e) {\n  if (e instanceof HTTPError && e.status === 404) return // idempotent success\n  throw e\n}","preventionTips":["Treat 404 on delete as success for idempotent retry flows","Re-read the doc to get a fresh _rev after concurrent edits","Disable duplicate delete buttons / debounce delete requests"],"tags":["oauth2","not-found","delete"],"backgroundTag":"resource-not-found","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}