{"record":{"id":"cf379f75e513fef3","repo":"pentaho/pentaho-kettle","slug":"not-a-valid-id-specified-in-plugin-plugin","errorCode":null,"errorMessage":"Not a valid id specified in plugin :${plugin}","messagePattern":"Not a valid id specified in plugin :(.+?)","errorType":"exception","errorClass":"KettlePluginException","httpStatus":null,"severity":"critical","filePath":"core/src/main/java/org/pentaho/di/core/plugins/PluginRegistry.java","lineNumber":222,"sourceCode":"    }\n  }\n\n  public void addParentClassLoaderPatterns( PluginInterface plugin, String[] patterns ) {\n    lock.writeLock().lock();\n    try {\n      parentClassloaderPatternMap.put( plugin, patterns );\n    } finally {\n      lock.writeLock().unlock();\n    }\n  }\n\n  public void registerPlugin( Class<? extends PluginTypeInterface> pluginType, PluginInterface plugin )\n      throws KettlePluginException {\n    boolean changed = false; // Is this an add or an update?\n    lock.writeLock().lock();\n    try {\n      if ( plugin.getIds()[0] == null ) {\n        throw new KettlePluginException( \"Not a valid id specified in plugin :\" + plugin );\n      }\n\n      // Keep the list of plugins sorted by name...\n      //\n      Set<PluginInterface> list = pluginMap.computeIfAbsent( pluginType, k -> new TreeSet<>( Plugin.nullStringComparator ) );\n\n      if ( !list.add( plugin ) ) {\n        list.remove( plugin );\n        list.add( plugin );\n        changed = true;\n      }\n\n      //Clear the category map cache for this plugin type. We will only cache the values on calls to getCategories\n      categoryMap.remove( pluginType );\n      \n    } finally {\n      lock.writeLock().unlock();\n      Set<PluginTypeListener> listeners = this.listeners.get( pluginType );","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/core/plugins/PluginRegistry.java#L204-L240","documentation":"PluginRegistry.registerPlugin validates that a plugin being registered has a non-null first ID before inserting it into pluginMap. A null id[0] makes the plugin unkeyable, so KettlePluginException is thrown. This is a registry invariant enforced under the write lock during plugin registration (initial registration or updates).","triggerScenarios":"registerPlugin(pluginType, plugin) is called (from a plugin type's registration flow or a test/class-factory setup) with a PluginInterface whose getIds()[0] is null — typically built programmatically or produced by a plugin type whose ID extraction yielded null.","commonSituations":"Custom code constructs a PluginInterface via PluginImpl/Plugin without ids; a custom PluginType registers plugins after a failed ID extraction; plugin XML/annotation metadata lost its id field; tests registering hand-built Plugin objects omit the id.","solutions":["Ensure the PluginInterface is created with at least one non-null id (pass ids array with a value to the Plugin constructor).","Fix the plugin's annotation/manifest so extractID produces a value (see missing-plugin-id errors at scan time).","In custom plugin types, reject/repair null IDs before calling registerPlugin.","Log the offending plugin object to identify which plugin type/scan produced the null ID."],"exampleFix":"// before\nPlugin plugin = new Plugin(null, PluginCategoryClasses.CATEGORY_UTIL, ..., \"MyPlugin\");\nregistry.registerPlugin(StepPluginType.class, plugin);\n\n// after\nPlugin plugin = new Plugin(new String[] { \"MyPluginId\" }, PluginCategoryClasses.CATEGORY_UTIL, ..., \"MyPlugin\");\nregistry.registerPlugin(StepPluginType.class, plugin);","handlingStrategy":"validation","validationCode":"// Before calling registerPlugin, validate the id array\nif (plugin.getIds() == null || plugin.getIds().length == 0 || plugin.getIds()[0] == null) {\n  throw new IllegalArgumentException(\"Plugin has no id: \" + plugin.getName());\n}","typeGuard":"static boolean hasValidId(org.pentaho.di.core.plugins.PluginInterface p) {\n  return p != null && p.getIds() != null && p.getIds().length > 0 && p.getIds()[0] != null && !p.getIds()[0].isEmpty();\n}","tryCatchPattern":"try {\n  PluginRegistry.getInstance().registerPlugin(StepPluginType.class, plugin);\n} catch (KettlePluginException e) {\n  if (e.getMessage().startsWith(\"Not a valid id\")) {\n    log.error(\"Plugin registered without id: \" + plugin, e);\n  }\n}","preventionTips":["Always construct Plugin objects with a non-empty ids array.","Fail fast in custom PluginTypes when ID extraction yields null.","Log the full PluginInterface toString to identify the offending plugin.","Cover plugin registration in smoke tests that run at startup."],"tags":["kettle","plugins","registry","validation"],"backgroundTag":"empty-required-field","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}