{"record":{"id":"2272d2dbef914de2","repo":"apache/druid","slug":"group-mapping-s-already-exists","errorCode":null,"errorMessage":"Group mapping [%s] already exists.","messagePattern":"Group mapping \\[(.+?)\\] already exists\\.","errorType":"http","errorClass":"BasicSecurityDBResourceException","httpStatus":400,"severity":"error","filePath":"extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java","lineNumber":911,"sourceCode":"  private boolean deleteGroupMappingOnce(String prefix, String groupMappingName)\n  {\n    byte[] oldValue = getCurrentGroupMappingMapBytes(prefix);\n    Map<String, BasicAuthorizerGroupMapping> groupMappingMap = BasicAuthUtils.deserializeAuthorizerGroupMappingMap(objectMapper, oldValue);\n    if (groupMappingMap.get(groupMappingName) == null) {\n      throw new BasicSecurityDBResourceException(\"Group mapping [%s] does not exist.\", groupMappingName);\n    } else {\n      groupMappingMap.remove(groupMappingName);\n    }\n    byte[] newValue = BasicAuthUtils.serializeAuthorizerGroupMappingMap(objectMapper, groupMappingMap);\n    return tryUpdateGroupMappingMap(prefix, groupMappingMap, oldValue, newValue);\n  }\n\n  private boolean createGroupMappingOnce(String prefix, BasicAuthorizerGroupMapping groupMapping)\n  {\n    byte[] oldValue = getCurrentGroupMappingMapBytes(prefix);\n    Map<String, BasicAuthorizerGroupMapping> groupMappingMap = BasicAuthUtils.deserializeAuthorizerGroupMappingMap(objectMapper, oldValue);\n    if (groupMappingMap.get(groupMapping.getName()) != null) {\n      throw new BasicSecurityDBResourceException(\"Group mapping [%s] already exists.\", groupMapping.getName());\n    } else {\n      groupMappingMap.put(groupMapping.getName(), groupMapping);\n    }\n    byte[] newValue = BasicAuthUtils.serializeAuthorizerGroupMappingMap(objectMapper, groupMappingMap);\n    return tryUpdateGroupMappingMap(prefix, groupMappingMap, oldValue, newValue);\n  }\n\n  private boolean createRoleOnce(String prefix, String roleName)\n  {\n    byte[] oldValue = getCurrentRoleMapBytes(prefix);\n    Map<String, BasicAuthorizerRole> roleMap = BasicAuthUtils.deserializeAuthorizerRoleMap(objectMapper, oldValue);\n    if (roleMap.get(roleName) != null) {\n      throw new BasicSecurityDBResourceException(\"Role [%s] already exists.\", roleName);\n    } else {\n      roleMap.put(roleName, new BasicAuthorizerRole(roleName, null));\n    }\n    byte[] newValue = BasicAuthUtils.serializeAuthorizerRoleMap(objectMapper, roleMap);\n    return tryUpdateRoleMap(prefix, roleMap, oldValue, newValue);","sourceCodeStart":893,"sourceCodeEnd":929,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java#L893-L929","documentation":"BasicSecurityDBResourceException thrown by createGroupMappingOnce when a group mapping with the same name already exists in the authorizer's group-mapping map. Group mapping names are unique keys, so the coordinator rejects duplicate creation rather than overwriting existing permissions. Prevents silently replacing an LDAP group-to-role mapping.","triggerScenarios":"POST /druid-ext/basic-security/authorization/db/<authorizer>/groupMappings/<groupMappingName> with a name that already exists in that authorizer.","commonSituations":"Re-running a setup script that defines LDAP group mappings; copying mappings between clusters where the name already exists; wanting to change a mapping but calling create instead of update.","solutions":["Check GET /druid-ext/basic-security/authorization/db/<authorizer>/groupMappings/<name> before creating","To change an existing mapping, use the PUT/update endpoint rather than create","Make setup scripts idempotent by skipping existing mappings","Delete the existing mapping first if a clean re-create is truly intended"],"exampleFix":"// before\ncreateGroupMapping(\"default\", mapping); // fails if exists\n// after\nif (groupMappingExists(\"default\", mapping.getName())) {\n  updateGroupMapping(\"default\", mapping.getName(), mapping);\n} else {\n  createGroupMapping(\"default\", mapping);\n}","handlingStrategy":"validation","validationCode":"boolean exists = getGroupMappings(\"default\").stream()\n    .anyMatch(gm -> gm.equals(mapping.getName()));\nif (exists) { update instead of create }","typeGuard":null,"tryCatchPattern":"try {\n  client.createGroupMapping(\"default\", mapping);\n} catch (BasicSecurityDBResourceException e) {\n  if (e.getMessage().contains(\"already exists\")) {\n    client.updateGroupMapping(\"default\", mapping.getName(), mapping);\n  } else { throw e; }\n}","preventionTips":["Check existence before create; prefer update semantics for re-runs","Store group mapping definitions in config management for idempotency","Use distinct names per environment to avoid collisions"],"tags":["security","rest-api","duplicate-resource"],"backgroundTag":"file-already-exists","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}