{"record":{"id":"a7086e25dddd70fd","repo":"Budibase/budibase","slug":"group-not-found","errorCode":null,"errorMessage":"Group not found","messagePattern":"Group not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/pro/src/sdk/groups/groups.ts","lineNumber":334,"sourceCode":"    if (newCreators > 0) {\n      await quotas.addUsers(0, newCreators)\n    } else if (newCreators < 0) {\n      const creatorsCountAfterSave = await getCreatorsCountInGroup(group)\n      const creatorsToRemove = Math.abs(newCreators) - creatorsCountAfterSave\n      if (creatorsToRemove > 0) {\n        await quotas.removeUsers(0, creatorsToRemove)\n      }\n    }\n    return savedGroup\n  }\n}\n\nexport async function remove(groupId: string, revision: string) {\n  let group\n  try {\n    group = await db.groups.get(groupId)\n  } catch (err) {\n    throw new Error(\"Group not found\")\n  }\n\n  const isCreatorGroup = Object.values((group.roles || {}) as object).includes(\n    \"CREATOR\"\n  )\n  let recalculateCreatorsQuotasFn: () => Promise<void> | void = () => {}\n  if (isCreatorGroup) {\n    const globalDb = tenancy.getGlobalDB()\n    const usersInGroup = await db.groups.getGroupUsers(groupId)\n    const users = await Promise.all(\n      (usersInGroup as User[]).map(user => {\n        return globalDb.get<User>(user._id)\n      })\n    )\n    const usersWithoutGroup = users.map(user => ({\n      ...user,\n      userGroups: user.userGroups!.filter(grp => grp !== groupId),\n    }))","sourceCodeStart":316,"sourceCodeEnd":352,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/pro/src/sdk/groups/groups.ts#L316-L352","documentation":"remove(groupId, revision) fetches the group by id and, if db.groups.get throws for any reason (missing document, wrong id, DB access error), replaces the underlying error with a generic 'Group not found' Error. It is the SDK's guard so callers get a consistent signal that the group targeted for deletion does not exist.","triggerScenarios":"Calling remove() with a groupId that does not exist, was already deleted, or whose document cannot be read from the groups DB (including any error thrown by get, e.g. connectivity problems that get masked as 'not found').","commonSituations":"UI/API acting on a stale group list after another admin deleted the group; wrong id passed from a client cache; double-submitting a delete; database outage misreported as a missing group.","solutions":["Verify the groupId is correct and the group still exists before deleting (fetch the group first).","Handle the already-deleted case idempotently: catch the error and treat the group as removed if a second delete also reports not found.","If removals cluster together, refresh the group list before retrying so stale ids are cleared.","If the group exists but removal still fails, check DB connectivity — the generic message can mask underlying get() errors."],"exampleFix":"// before\nawait groups.remove(groupId, revision) // throws 'Group not found'\n// after\ntry {\n  await groups.remove(groupId, revision)\n} catch (err) {\n  if (err.message === \"Group not found\") {\n    // already deleted — treat as success / refresh list\n  } else {\n    throw err\n  }\n}","handlingStrategy":"try-catch","validationCode":"let exists = true\ntry { await db.groups.get(groupId) } catch { exists = false }\nif (!exists) { /* skip removal or refresh group list */ }","typeGuard":null,"tryCatchPattern":"try {\n  await groups.remove(groupId, revision)\n} catch (err) {\n  if (err.message === \"Group not found\") {\n    // idempotent: treat as already deleted\n  } else throw err\n}","preventionTips":["Refresh cached group lists before delete operations","Make deletes idempotent to survive double-submissions","Log the original underlying error if 'not found' could mask DB outages"],"tags":["groups","not-found","crud"],"backgroundTag":"resource-not-found","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}