quarkusio/quarkus · error · RuntimeException
Invalid proxy passed to recorder. ${rp} was created in a run
Error message
Invalid proxy passed to recorder. ${rp} 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. What it means
Recorder objects returned by runtime-recorder methods are stored in the StartupContext and only exist when runtime init runs. A recorder executing in STATIC_INIT runs before that, so passing such a proxy into a static-init recorder would reference an object that does not exist yet. Quarkus detects this via the proxy's __static$$init flag and fails the build with this message.
Source
Thrown at core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java:735
return value;
}
};
} else if (param instanceof Enum) {
Enum e = (Enum) param;
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
ResultHandle nm = method.load(e.name());
return method.invokeStaticMethod(
ofMethod(e.getDeclaringClass(), "valueOf", e.getDeclaringClass(), String.class),
nm);
}
};
} else if (param instanceof ReturnedProxy) {
//if this is a proxy we just grab the value from the StartupContext
ReturnedProxy rp = (ReturnedProxy) param;
if (!rp.__static$$init() && staticInit) {
throw new RuntimeException("Invalid proxy passed to recorder. " + rp
+ " 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.");
}
String proxyId = rp.__returned$proxy$key();
//because this is the result of a method invocation that may not have happened at param deserialization time
//we just load it from the startup context
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.invokeVirtualMethod(ofMethod(StartupContext.class, "getValue", Object.class, String.class),
method.getMethodParam(0), method.load(proxyId));
}
};
} else if (param instanceof Duration) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.invokeStaticMethod(ofMethod(Duration.class, "parse", Duration.class, CharSequence.class),
method.load(param.toString()));View on GitHub (pinned to e1c734241f)
Solutions
- Change the consuming recorder to @Record(ExecutionTime.RUNTIME_INIT) so it runs when the proxy exists
- Obtain the value at STATIC_INIT time instead: call the producer recorder method in a STATIC_INIT @Record context
- Restructure so the static-init recorder only receives build-time-serializable values, and the runtime value is consumed in a runtime recorder
- Check the producer recorder's phase — a value you assume is static-init may come from RUNTIME_INIT
- If using RuntimeValue, ensure the underlying recorder method was recorded in the same phase as the consumer
Example fix
// before
@Record(ExecutionTime.STATIC_INIT)
void init(MyRecorder r, RuntimeValue<BeanContainer> bc) { ... } // bc from runtime recorder
// after
@Record(ExecutionTime.RUNTIME_INIT)
void init(MyRecorder r, RuntimeValue<BeanContainer> bc) { ... } Defensive patterns
Strategy: type-guard
Validate before calling
// before passing a proxy into a static-init recorder
if (proxy instanceof io.quarkus.runtime.annotations.ReturnedProxy rp && !rp.__static$$init()) {
throw new IllegalStateException("Runtime-init proxy cannot be consumed by a STATIC_INIT recorder");
} Type guard
static boolean isStaticInitSafe(Object proxy) {
return !(proxy instanceof io.quarkus.runtime.annotations.ReturnedProxy p) || p.__static$$init();
} Prevention
- Match recorder phases: only pass STATIC_INIT-produced proxies to STATIC_INIT recorders
- Consume RUNTIME_INIT values only from RUNTIME_INIT @Record methods
- Double-check @Record(ExecutionTime.X) on both producer and consumer when wiring recorders
When it happens
Trigger: In a @Recorder(Phase.STATIC_INIT) recorder method, a parameter is a proxy returned from another recorder that ran in Phase.RUNTIME_INIT (e.g. a RuntimeValue<BeanContainer> or proxy from a runtime-init record method).
Common situations: Extension developers wiring runtime-init results (CDI bean containers, HTTP handlers, clients created at runtime) into static-init recorders; copying recorder code between phases without adjusting the phase; upstream extension changing a recorder's phase.
Related errors
- Invalid proxy passed to recorder. Parameter ${i} of type ${m
- %s is marked @Record but does not inject an @Recorder object
- Unknown recorder constructor parameter: %s in recorder %s
- Public method ${methodInfo} cannot be proxied as it is final
- Cannot use ${method} as a recorder method as the return type
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/7394cb2c7e70705e.
Report an issue: GitHub.