{"record":{"id":"1ed37a000977083b","repo":"quarkusio/quarkus","slug":"unable-to-serialize-param-as-the-wrong-number-o","errorCode":null,"errorMessage":"Unable to serialize ${param} as the wrong number of parameters were generated for ${nonDefaultConstructorHolder.constructor}","messagePattern":"Unable to serialize (.+?) as the wrong number of parameters were generated for (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java","lineNumber":1223,"sourceCode":"                        key.prepare(context);\n                        val.prepare(context);\n                    }\n                });\n            }\n            relaxedOk = true;\n        }\n\n        //check how the object is constructed\n        NonDefaultConstructorHolder nonDefaultConstructorHolder = null;\n        DeferredParameter[] nonDefaultConstructorHandles = null;\n        //used to resolve the parameter position for @RecordableConstructor\n        Map<String, Integer> constructorParamNameMap = new HashMap<>();\n\n        if (nonDefaultConstructors.containsKey(param.getClass())) {\n            nonDefaultConstructorHolder = nonDefaultConstructors.get(param.getClass());\n            List<Object> params = nonDefaultConstructorHolder.paramGenerator.apply(param);\n            if (params.size() != nonDefaultConstructorHolder.constructor.getParameterCount()) {\n                throw new RuntimeException(\"Unable to serialize \" + param\n                        + \" as the wrong number of parameters were generated for \"\n                        + nonDefaultConstructorHolder.constructor);\n            }\n            int count = 0;\n            nonDefaultConstructorHandles = new DeferredParameter[params.size()];\n            Class<?>[] parameterTypes = nonDefaultConstructorHolder.constructor.getParameterTypes();\n            for (int i = 0; i < params.size(); i++) {\n                Object obj = params.get(i);\n                nonDefaultConstructorHandles[i] = loadObjectInstance(obj, existing,\n                        parameterTypes[count++], relaxedValidation);\n            }\n            extractConstructorParameterNames(nonDefaultConstructorHolder.constructor, constructorParamNameMap);\n        } else if (classesToUseRecordableConstructor.contains(param.getClass())) {\n            Constructor<?> current = null;\n            int count = 0;\n            for (var c : param.getClass().getConstructors()) {\n                if (current == null || current.getParameterCount() < c.getParameterCount()) {\n                    current = c;","sourceCodeStart":1205,"sourceCodeEnd":1241,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java#L1205-L1241","documentation":"Bytecode recording of a non-default-constructor object requires a registered paramGenerator that produces exactly the constructor's arguments at record time. When the generator returns a list whose size differs from the constructor's parameter count, serialization is aborted. This is a bug in the extension that registered the constructor (via io.quarkus.deployment.recording.RecordableConstructor or ConstructorParameterCustomizer-style registration), not typically a user config mistake.","triggerScenarios":"Registering a non-default constructor for a class where the supplied paramGenerator function (e.g. via RecordingAnnotations/ConstructorParameterCustomizer or a returned non-serializable object with a RecordableConstructor) returns fewer or more values than constructor.getParameterCount(); commonly after the class gains a new constructor parameter while the generator was not updated.","commonSituations":"Extension code evolved: a Quarkus upgrade changed a config-class constructor signature while custom param generation logic in the extension was left stale; hand-written paramGenerator with a hardcoded list of values.","solutions":["Update the registered paramGenerator to return exactly one value per constructor parameter, in order","Recompile against the current constructor signature (parameter count/types may have changed in a newer Quarkus or extension version)","Add an assertion/unit test that params.size() == constructor.getParameterCount() for your generated class"],"exampleFix":"// before\nholder.paramGenerator = obj -> List.of(obj.getName());\n// after (ctor is Foo(String name, int size))\nholder.paramGenerator = obj -> List.of(obj.getName(), obj.getSize());","handlingStrategy":"validation","validationCode":"List<Object> params = holder.paramGenerator.apply(instance);\nif (params.size() != holder.constructor.getParameterCount()) {\n    throw new IllegalStateException(\"paramGenerator/constructor arity mismatch for \"\n        + holder.constructor.getDeclaringClass());\n}","typeGuard":"boolean arityOk(Function<Object,List<Object>> gen, Constructor<?> c, Object o) {\n    return gen.apply(o).size() == c.getParameterCount();\n}","tryCatchPattern":"try {\n    recorder.record(instance);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"wrong number of parameters\")) {\n        throw new IllegalStateException(\"Fix registered paramGenerator for \" + instance.getClass(), e);\n    }\n    throw e;\n}","preventionTips":["Unit-test every registered paramGenerator against its constructor's parameter count","Derive generator output from the constructor signature, not hardcoded lists","Add a build-time assertion in the extension that validates generator arity"],"tags":["quarkus","bytecode-recording","constructor"],"backgroundTag":"bytecode-recording-constructor-mismatch","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}