{"record":{"id":"1d96ef1ebf254561","repo":"apache/druid","slug":"class-s-does-not-implement-s","errorCode":null,"errorMessage":"Class [%s] does not implement %s","messagePattern":"Class \\[(.+?)\\] does not implement (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/guice/DruidInjectorBuilder.java","lineNumber":174,"sourceCode":"    return this;\n  }\n\n  public DruidInjectorBuilder addClass(Class<?> input)\n  {\n    if (!acceptModule(input)) {\n      return this;\n    }\n    if (DruidModule.class.isAssignableFrom(input)) {\n      @SuppressWarnings(\"unchecked\")\n      DruidModule module = baseInjector.getInstance((Class<? extends DruidModule>) input);\n      registerJacksonModules(module);\n      modules.add(module);\n    } else if (Module.class.isAssignableFrom(input)) {\n      @SuppressWarnings(\"unchecked\")\n      Module module = baseInjector.getInstance((Class<? extends Module>) input);\n      modules.add(module);\n    } else {\n      throw new ISE(\"Class [%s] does not implement %s\", input, Module.class);\n    }\n    return this;\n  }\n\n  /**\n   * Filter module classes based on the (optional) module exclude list and\n   * (optional) set of known node roles.\n   */\n  private boolean acceptModule(Class<?> moduleClass)\n  {\n    // Modules config is optional: it won't be present in tests or clients.\n    String moduleClassName = moduleClass.getName();\n    if (moduleClassName != null && modulesConfig.getExcludeList().contains(moduleClassName)) {\n      log.info(\"Not loading module %s because it is present in excludeList\", moduleClassName);\n      return false;\n    }\n\n    // Tests don't have node roles, and so want to load the given modules","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/guice/DruidInjectorBuilder.java#L156-L192","documentation":"DruidInjectorBuilder.addClass validates that the supplied Class implements com.google.inject.Module; if not, instantiation is impossible in the injector pipeline and an ISE is thrown naming the class and required interface.","triggerScenarios":"Calling addInput(SomeClass.class) (leading to addClass) where SomeClass does not implement Module; addInput is its caller in the builder chain.","commonSituations":"Typos passing the wrong class (a config class instead of a module); refactors removing `implements Module` from a class still registered in initialization lists; tests registering non-module classes.","solutions":["Make the class implement Module (or extend DruidModule)","Register a different class that actually implements Module","If the class is a plain binding target, add it inside a module's configure() instead of addInput"],"exampleFix":"// before\nbuilder.addClass(MyConfig.class); // not a Module\n// after\nbuilder.addClass(MyConfigModule.class); // implements DruidModule","handlingStrategy":"type-guard","validationCode":"if (!Module.class.isAssignableFrom(clazz)) {\n  throw new IllegalArgumentException(clazz + \" does not implement Module\");\n}","typeGuard":"static boolean isModuleClass(Class<?> c) {\n  return Module.class.isAssignableFrom(c);\n}","tryCatchPattern":"try {\n  builder.addClass(clazz);\n} catch (IllegalStateException e) {\n  if (e.getMessage().contains(\"does not implement\")) {\n    log.error(\"Class \" + clazz + \" must implement Module\");\n  }\n  throw e;\n}","preventionTips":["Have all registered modules implement DruidModule/Module","Run a startup test that builds the injector from the full module list","Grep initialization lists when removing `implements Module` from a class"],"tags":["dependency-injection","api-misuse","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}