{"record":{"id":"cf6d5899bc16f82d","repo":"quarkusio/quarkus","slug":"cannot-use-method-as-a-recorder-method-as-the-r","errorCode":null,"errorMessage":"Cannot use ${method} as a recorder method as the return type cannot be proxied. Use RuntimeValue to wrap the return value instead.","messagePattern":"Cannot use (.+?) as a recorder method as the return type cannot be proxied\\. Use RuntimeValue to wrap the return value instead\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java","lineNumber":353,"sourceCode":"                                throw new RuntimeException(\"Invalid proxy passed to recorder. Parameter \" + i + \" of type \"\n                                        + method.getParameterTypes()[i]\n                                        + \" was created in a runtime recorder method, while this recorder is for a static init method. The object will not have been created at the time this method is run.\");\n                            }\n                        }\n                    }\n                }\n                StoredMethodCall storedMethodCall = new StoredMethodCall(theClass, method, args);\n                storedMethodCalls.add(storedMethodCall);\n                Class<?> returnType = method.getReturnType();\n                if (method.getName().equals(\"toString\")\n                        && method.getParameterCount() == 0\n                        && returnType.equals(String.class)) {\n                    return proxy.getClass().getName();\n                }\n\n                boolean voidMethod = method.getReturnType().equals(void.class);\n                if (!voidMethod && !isProxiable(method.getReturnType())) {\n                    throw new RuntimeException(\"Cannot use \" + method\n                            + \" as a recorder method as the return type cannot be proxied. Use RuntimeValue to wrap the return value instead.\");\n                }\n                if (voidMethod) {\n                    return null;\n                }\n                ProxyInstance instance = getProxyInstance(returnType);\n                if (instance == null) {\n                    return null;\n                }\n\n                storedMethodCall.returnedProxy = instance.proxy;\n                storedMethodCall.proxyId = instance.key;\n                return instance.proxy;\n            }\n\n        };\n\n        try {","sourceCodeStart":335,"sourceCodeEnd":371,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java#L335-L371","documentation":"Recorder methods' return values must be either void, primitively recordable, or proxyable types that Quarkus can substitute with recorded proxies. If a recorder method returns a non-proxiable type (e.g. final class, primitive-wrapper misuse, unrecordable class), invoke() cannot represent the result, so it fails with guidance to wrap the value in RuntimeValue instead.","triggerScenarios":"Declaring a @Recorder method whose return type fails isProxiable(): final classes, classes without usable constructors, or other non-recordable types returned directly instead of RuntimeValue<T>.","commonSituations":"Returning configuration POJOs/records/final builder classes from recorders; returning Optional or streams; returning third-party final classes from recorder APIs.","solutions":["Change the recorder method return type to RuntimeValue<T> and construct with new RuntimeValue<>(instance)","Return an interface or non-final class that Quarkus can proxy","Return a primitive/String/List/Map directly if the data is recordable","Split the method: return recordable data, and do non-recordable work inside the recorder method itself"],"exampleFix":"// before\n@Recorder\npublic MyFinalService service() { return new MyFinalService(); }\n// after\n@Recorder\npublic RuntimeValue<MyFinalService> service() { return new RuntimeValue<>(new MyFinalService()); }","handlingStrategy":"type-guard","validationCode":"static boolean recorderReturnOk(Class<?> t) {\n    return t.isPrimitive() || t == void.class || t == String.class || !Modifier.isFinal(t.getModifiers());\n}","typeGuard":"static boolean recorderReturnOk(Class<?> t) {\n    return t.isPrimitive() || t == void.class || t == String.class || !Modifier.isFinal(t.getModifiers());\n}","tryCatchPattern":null,"preventionTips":["Default to RuntimeValue<T> for complex recorder return types","Avoid final classes and records as direct recorder return values","Add a build-time test invoking each recorder method signature"],"tags":["recorder","runtimevalue","proxy","build-time"],"backgroundTag":"non-proxiable-recorder-return-type","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}