{"record":{"id":"b694368ddf59b8b8","repo":"SonarSource/sonarqube","slug":"unexpected-node-type","errorCode":null,"errorMessage":"Unexpected node type ","messagePattern":"Unexpected node type ","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server/sonar-main/src/main/java/org/sonar/application/config/ClusterSettings.java","lineNumber":278,"sourceCode":"  /**\n   * Hazelcast must be started when cluster is activated on all nodes but search ones\n   */\n  public static boolean shouldStartHazelcast(AppSettings appSettings) {\n    return isClusterEnabled(appSettings.getProps()) && toNodeType(appSettings.getProps()).equals(NodeType.APPLICATION);\n  }\n\n  public static List<ProcessId> getEnabledProcesses(AppSettings settings) {\n    if (!isClusterEnabled(settings)) {\n      return asList(ProcessId.ELASTICSEARCH, ProcessId.WEB_SERVER, ProcessId.COMPUTE_ENGINE);\n    }\n    NodeType nodeType = NodeType.parse(settings.getValue(CLUSTER_NODE_TYPE.getKey()).orElse(\"\"));\n    switch (nodeType) {\n      case APPLICATION:\n        return asList(ProcessId.WEB_SERVER, ProcessId.COMPUTE_ENGINE);\n      case SEARCH:\n        return singletonList(ProcessId.ELASTICSEARCH);\n      default:\n        throw new IllegalArgumentException(\"Unexpected node type \" + nodeType);\n    }\n  }\n\n  public static boolean isLocalElasticsearchEnabled(AppSettings settings) {\n    // elasticsearch is enabled on \"search\" nodes, but disabled on \"application\" nodes\n    if (isClusterEnabled(settings.getProps())) {\n      return NodeType.parse(settings.getValue(CLUSTER_NODE_TYPE.getKey()).orElse(\"\")) == NodeType.SEARCH;\n    }\n\n    // elasticsearch is enabled in standalone mode\n    return true;\n  }\n}\n","sourceCodeStart":260,"sourceCodeEnd":292,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-main/src/main/java/org/sonar/application/config/ClusterSettings.java#L260-L292","documentation":"ClusterSettings.getEnabledProcesses maps a NodeType to the processes that node should run. The switch handles APPLICATION and SEARCH and throws an IllegalArgumentException 'Unexpected node type ' for anything else. Given toNodeType validates input against NodeType beforehand, reaching the default branch indicates an internal invariant violation (an unknown enum constant), not operator error.","triggerScenarios":"getEnabledProcesses throws only if nodeType is a NodeType value not covered by the switch — practically impossible via normal configuration since toNodeType already restricted values to APPLICATION/SEARCH; it can only occur with a new NodeType enum value added without updating this switch, or via direct programmatic invocation.","commonSituations":"Custom patches or forks adding a NodeType constant (e.g. a new node role) without extending getEnabledProcesses; version skew where a plugin/patch introduces an enum value the running switch does not know.","solutions":["If you patched SonarQube and added a NodeType constant, extend the switch in getEnabledProcesses to return the correct process list for it.","Otherwise this is a bug: report it with the full stack trace and the exact sonar-cluster node type value logged.","As a workaround, revert to a supported node type ('application' or 'search') in sonar.cluster.node.type."],"exampleFix":"// before\nswitch (nodeType) {\n  case APPLICATION: return asList(ProcessId.WEB_SERVER, ProcessId.COMPUTE_ENGINE);\n  case SEARCH: return singletonList(ProcessId.ELASTICSEARCH);\n  default: throw new IllegalArgumentException(\"Unexpected node type \" + nodeType);\n}\n\n// after (when adding a new node type)\nswitch (nodeType) {\n  case APPLICATION: return asList(ProcessId.WEB_SERVER, ProcessId.COMPUTE_ENGINE);\n  case SEARCH: return singletonList(ProcessId.ELASTICSEARCH);\n  case NEW_ROLE: return asList(ProcessId.WEB_SERVER);\n  default: throw new IllegalArgumentException(\"Unexpected node type \" + nodeType);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  List<ProcessId> processes = ClusterSettings.getEnabledProcesses(settings);\n} catch (IllegalArgumentException e) {\n  // internal invariant broken: unknown NodeType reached the switch\n  log.error(\"Unknown node type; verify no custom NodeType enum values were added without updating getEnabledProcesses\", e);\n  throw e;\n}","preventionTips":["Do not add NodeType enum constants via patches without updating every switch on NodeType","Treat this as a bug to report, not a configuration problem","Keep node type values restricted to 'application' and 'search' in all configs"],"tags":["internal","invariant","cluster","sonarqube"],"backgroundTag":"internal-invariant-violation","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}