🔴 The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
// CAPTURED — OpenJDK 25 Temurin: classfile hand-built so the MethodParameters
// attribute cites constant-pool index 600 (> cp_count); probed via getParameters().
Exception in thread "main" java.lang.reflect.MalformedParametersException: Invalid constant pool index
at java.base/java.lang.reflect.Executable.parameterData(Executable.java:475)
at java.base/java.lang.reflect.Executable.getParameters(Executable.java:419)
at com.devinhyderabad.cli.CommandRegistry.register(CommandRegistry.java:26)⚡ Quick Fix Works 80% of the time
Compile EVERY module with the same -parameters flag — mixed settings produce attributes some tools cannot validate.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<parameters>true</parameters>
</configuration>
</plugin>🧠 Why this Happens
Tap to expand the deep technical explanation
With javac -parameters, the compiler writes a MethodParameters attribute mapping local slots to parameter NAMES for reflection. Verified chain on OpenJDK 25: getParameters() delegates to Executable.parameterData, whose native parse throws IllegalArgumentException for an out-of-range constant-pool reference; the catch block rethrows it as MalformedParametersException with the message "Invalid constant pool index" (no index number — :475). The verifyParameters companion adds the wrong-count (:438), invalid-parameter-name (:448) and invalid-modifiers (:453) variants. Valid compilers never produce any of these; inconsistency does: one module built with -parameters then processed by an older bytecode tool, hand-edited classfiles, or processors emitting half-valid attributes. Spring's parameter-name discovery walks this exact path, so broken attributes surface as wiring failures far from the cause.
The HITEC City Parking Spot Analogy:
A phone book listing extension numbers for desks that do not exist — the operator (JVM) refuses the whole lookup instead of guessing which desk you meant.
🔁 How to Reproduce Confirm this is your error
Compile any class with javac -parameters, then rewrite one name_index inside its MethodParameters attribute to an out-of-range slot (hex-patch to 0xFFFE) and call method.getParameters(). Patching trap: parameters_count packs as ONE byte (JVMS 4.7.24), so attribute_length = 1 + 4*N — patches assuming 2 + 4*N corrupt the classfile instead. (Captured on OpenJDK 25 Temurin; layout identical on 17.)
🛠️ Solutions (5 Ways to Fix)
Compile every module with -parameters uniformly
👉 Use this when/if multi-module builds mix annotated and non-annotated compilation units.
Set the flag ONCE in the shared parent POM (or convention plugin) so all modules emit consistent MethodParameters attributes. Half-on configurations are the primary source of validation failures downstream.
<!-- root pom.xml — inherited everywhere -->
<properties>
<maven.compiler.parameters>true</maven.compiler.parameters>
</properties>Align bytecode-processing tool versions
👉 Use this when/if Lombok, ASM transforms, Gradle instrumentation, or shading sit between javac and runtime.
Tools that rewrite classfiles must preserve (or correctly regenerate) the MethodParameters attribute. Stale ASM versions truncate unknown attributes — upgrade every transformer to a release matching your JDK.
./gradlew build --refresh-dependencies
# verify post-transform attributes survive:
javap -v build/libs/app.jar | grep -A2 MethodParametersSpring Boot specifics: let the plugin set -parameters
👉 Use this when/if @RequestParam/@PathVariable binding resolves arg0..argN or throws during context startup.
spring-boot-maven-plugin and the Gradle equivalent automatically add -parameters. Bypassing Boot's parent/plugin (custom compiler config, shade repackaging) silently loses the flag — restore it explicitly and remove obsolete LocalVariableTableParameterNameDiscoverer reliance (removed in Framework 6.1).
<!-- if NOT inheriting spring-boot-starter-parent -->
<configuration>
<compilerArgs><arg>-parameters</arg></compilerArgs>
</configuration>Inspect suspect classfiles with javap -v
👉 Use this when/if you need proof of WHICH build step damaged the attribute.
javap -v prints MethodParameters entries with their constant-pool references. Comparing output before and after each pipeline stage pinpoints the mangler — evidence for vendor bugs and for your own pipeline fixes.
javap -v -p target/classes/com/devinhyderabad/cli/Command.class \
| grep -B2 -A4 MethodParametersLibrary-side catch-and-degrade to synthetic names
👉 Use this when/if your framework introspects arbitrary user code that may arrive pre-damaged.
Wrap getParameters() defensively: on MalformedParametersException log the offending class and fall back to positional names (arg0..argN) or configuration-supplied names, keeping the application bootable while the real fix lands upstream.
Parameter[] params;
try {
params = method.getParameters();
} catch (MalformedParametersException e) {
log.warn("Bad MethodParameters on {}; positional fallback", method);
params = null; // caller uses index-based binding
}📋 Version Notes
MethodParameters attribute and the -parameters flag introduced together.
Mixed-toolchain builds begin producing inconsistent attributes.
Spring Framework 6.1 removes bytecode-parsing name fallbacks — -parameters becomes mandatory for injection-by-name.
Validation strictness identical across modern JDKs.
🛡️ How to Prevent This Next Time
Centralize compiler flags in the parent build, keep every bytecode transformer on a JDK-current release, add a CI step asserting parameter names survive packaging (javap grep), and prefer constructor binding over reflective name discovery where possible.