{"record":{"id":"d73b64c7e54b1a65","repo":"apache/druid","slug":"couldn-t-serialize-authorizer-rolemap","errorCode":null,"errorMessage":"Couldn't serialize authorizer roleMap!","messagePattern":"Couldn't serialize authorizer roleMap!","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicAuthUtils.java","lineNumber":236,"sourceCode":"      roleMap = new HashMap<>();\n    } else {\n      try {\n        roleMap = objectMapper.readValue(roleMapBytes, BasicAuthUtils.AUTHORIZER_ROLE_MAP_TYPE_REFERENCE);\n      }\n      catch (IOException ioe) {\n        throw new RuntimeException(\"Couldn't deserialize authorizer roleMap!\", ioe);\n      }\n    }\n    return roleMap;\n  }\n\n  public static byte[] serializeAuthorizerRoleMap(ObjectMapper objectMapper, Map<String, BasicAuthorizerRole> roleMap)\n  {\n    try {\n      return objectMapper.writeValueAsBytes(roleMap);\n    }\n    catch (IOException ioe) {\n      throw new ISE(ioe, \"Couldn't serialize authorizer roleMap!\");\n    }\n  }\n\n  public static void maybeInitialize(final RetryUtils.Task<?> task)\n  {\n    try {\n      RetryUtils.retry(task, SHOULD_RETRY_INIT, MAX_INIT_RETRIES);\n    }\n    catch (Exception e) {\n      throw new RuntimeException(e);\n    }\n  }\n}\n","sourceCodeStart":218,"sourceCodeEnd":250,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicAuthUtils.java#L218-L250","documentation":"Thrown by BasicAuthUtils.serializeAuthorizerRoleMap when Jackson fails to convert the authorizer role map into bytes for persistence or cache notification. The role map is serialized whenever roles are updated and pushed to metadata storage; an IOException on that write surfaces as this ISE. Root causes are almost always in the object graph or the mapper configuration.","triggerScenarios":"Calling serializeAuthorizerRoleMap with a Map<String, BasicAuthorizerRole> containing values Jackson cannot serialize (custom role subclasses without serializers, cyclic references, or a mapper missing required modules).","commonSituations":"Custom BasicAuthorizerRole implementations lacking Jackson annotations; passing a freshly constructed ObjectMapper instead of the injected Druid jsonMapper; corrupted in-memory role maps after failed updates.","solutions":["Inspect the wrapped IOException cause to identify the failing field/type","Use the fully configured Druid ObjectMapper rather than a bare new ObjectMapper()","Fix custom role classes' Jackson annotations (add @JsonProperty/@JsonTypeInfo as needed)","Retry the role update once state is corrected"],"exampleFix":"// before\nreturn objectMapper.writeValueAsBytes(roleMap);\n// after\ntry { return objectMapper.writeValueAsBytes(roleMap); } catch (IOException ioe) { throw new ISE(ioe, \"Couldn't serialize authorizer roleMap!\"); } // ensure objectMapper is the Druid-injected mapper","handlingStrategy":"try-catch","validationCode":"if (roleMap == null || roleMap.isEmpty()) { throw new IllegalArgumentException(\"roleMap empty\"); }\nroleMap.values().forEach(r -> { if (!jsonMapper.canSerialize(r.getClass())) { throw new IllegalArgumentException(\"unserializable role class: \" + r.getClass()); } });","typeGuard":"boolean isSerializable(ObjectMapper m, Object o) { return m.canSerialize(o.getClass()); }","tryCatchPattern":"try { byte[] b = BasicAuthUtils.serializeAuthorizerRoleMap(jsonMapper, roleMap); } catch (ISE e) { LOG.error(e, \"roleMap serialization failed\"); throw e; }","preventionTips":["Use the injected Druid ObjectMapper for all role-map serialization","Add round-trip serialize/deserialize tests for custom BasicAuthorizerRole subclasses","Avoid storing transient/contextual state inside role objects","Log the wrapped IOException cause for quick diagnosis"],"tags":["jackson","serialization","basic-security"],"backgroundTag":"json-marshal-failed","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}