{"record":{"id":"12f08de0dcf10fb9","repo":"apache/cassandra","slug":"failed-to-validate-custom-indexer-options-optio","errorCode":null,"errorMessage":"Failed to validate custom indexer options: ${options}","messagePattern":"Failed to validate custom indexer options: (.+?)","errorType":"validation","errorClass":"ConfigurationException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/schema/IndexMetadata.java","lineNumber":199,"sourceCode":"            {\n                unknownOptions = (Map) indexerClass.getMethod(\"validateOptions\", Map.class).invoke(null, filteredOptions);\n            }\n\n            if (!unknownOptions.isEmpty())\n                throw new ConfigurationException(String.format(\"Properties specified %s are not understood by %s\", unknownOptions.keySet(), indexerClass.getSimpleName()));\n        }\n        catch (NoSuchMethodException e)\n        {\n            logger.info(\"Indexer {} does not have a static validateOptions method. Validation ignored\",\n                        indexerClass.getName());\n        }\n        catch (InvocationTargetException e)\n        {\n            if (e.getTargetException() instanceof InvalidRequestException)\n                throw (InvalidRequestException) e.getTargetException();\n            if (e.getTargetException() instanceof ConfigurationException)\n                throw (ConfigurationException) e.getTargetException();\n            throw new ConfigurationException(\"Failed to validate custom indexer options: \" + options);\n        }\n        catch (ConfigurationException e)\n        {\n            throw e;\n        }\n        catch (Exception e)\n        {\n            throw new ConfigurationException(\"Failed to validate custom indexer options: \" + options);\n        }\n    }\n\n    public boolean isCustom()\n    {\n        return kind == Kind.CUSTOM;\n    }\n\n    public boolean isKeys()\n    {","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/schema/IndexMetadata.java#L181-L217","documentation":"Wraps an unexpected failure thrown by a custom indexer's static validateOptions method. When the reflective InvocationTargetException target is neither InvalidRequestException nor ConfigurationException (e.g. NullPointerException, ClassCastException in plugin code), Cassandra rethrows it as a ConfigurationException that only echoes the options map. It signals the indexer plugin itself crashed during validation, not that a specific option was wrong.","triggerScenarios":"CREATE/ALTER CUSTOM INDEX with USING '<class>' + OPTIONS where the indexer's validateOptions(Map) throws an unchecked exception or an exception type other than InvalidRequestException/ConfigurationException; the target exception's cause is swallowed and only the options string is reported.","commonSituations":"Buggy third-party indexer plugin code (NPE when an expected option is absent), indexer expecting a complex value and calling methods on it that throw, plugin version incompatible with the Cassandra version's validateOptions contract.","solutions":["Check the node/system log around this error for the wrapped cause; the message hides the underlying stack trace.","Ensure all options the indexer requires (beyond class_name) are present and of expected types.","Fix or replace the indexer plugin; verify plugin compatibility with your Cassandra version.","If you maintain the indexer, throw only InvalidRequestException or ConfigurationException so messages propagate correctly."],"exampleFix":"// indexer plugin before\npublic static Map<String,String> validateOptions(Map<String,String> options) { return check(options.get(\"required_opt\").trim()); } // NPE if absent\n// after\npublic static Map<String,String> validateOptions(Map<String,String> options) { if (!options.containsKey(\"required_opt\")) throw new ConfigurationException(\"required_opt must be specified\"); return check(options.get(\"required_opt\").trim()); }","handlingStrategy":"try-catch","validationCode":"Map<String,String> copy = new HashMap<>(options); copy.remove(\"class_name\"); // smoke-test the plugin's validateOptions before DDL\nObject r = indexerClass.getMethod(\"validateOptions\", Map.class).invoke(null, copy); // throws early if plugin is broken","typeGuard":null,"tryCatchPattern":"try { createCustomIndex(cls, options); } catch (ConfigurationException e) { if (e.getMessage().startsWith(\"Failed to validate custom indexer options\")) inspectPluginLogsForCause(); else throw e; }","preventionTips":["Unit-test your indexer plugin's validateOptions with the exact option maps used in production DDL.","Throw only InvalidRequestException/ConfigurationException from validateOptions so messages survive the wrapper.","Keep the plugin JAR version in sync across all nodes."],"tags":["cassandra","index","plugin","reflection"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}