pinpoint-apm/pinpoint · error · RuntimeException
${after} must return java.lang.Object. ${targetInterceptorCl
Error message
${after} must return java.lang.Object. ${targetInterceptorClazz.getName()} What it means
For InterceptorType.RESULT_REPLACE interceptors, the factory requires the after method to return java.lang.Object. A covariant return type (e.g. String) would change the weaved call descriptor and break the generated INVOKEINTERFACE call site, so the factory throws eagerly at definition time.
Source
Thrown at agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InterceptorDefinitionFactory.java:194
}
private InterceptorDefinition createInterceptorDefinition(Class<? extends Interceptor> targetInterceptorClazz) {
final Method beforeMethod = searchMethod(targetInterceptorClazz, before, beforeParamList);
if (beforeMethod == null) {
throw new RuntimeException(before + " method not found. " + Arrays.toString(beforeParamList));
}
final boolean beforeIgnoreMethod = beforeMethod.isAnnotationPresent(IgnoreMethod.class);
final boolean blockType = beforeMethod.getReturnType() == TraceBlock.class;
final Method afterMethod = searchMethod(targetInterceptorClazz, after, afterParamList);
if (afterMethod == null) {
throw new RuntimeException(after + " method not found. " + Arrays.toString(afterParamList));
}
final boolean afterIgnoreMethod = afterMethod.isAnnotationPresent(IgnoreMethod.class);
if (interceptorType == InterceptorType.RESULT_REPLACE && afterMethod.getReturnType() != Object.class) {
// a covariant override would change the weaved call descriptor and break the INVOKEINTERFACE site.
throw new RuntimeException(after + " must return java.lang.Object. " + targetInterceptorClazz.getName());
}
if (beforeIgnoreMethod && afterIgnoreMethod) {
return new DefaultInterceptorDefinition(interceptorClazz, targetInterceptorClazz, interceptorType, CaptureType.NON, null, null);
}
if (beforeIgnoreMethod) {
if (blockType) {
throw new RuntimeException(before + " not allowed return. " + Arrays.toString(beforeParamList));
}
return new DefaultInterceptorDefinition(interceptorClazz, targetInterceptorClazz, interceptorType, CaptureType.AFTER, null, afterMethod);
}
if (afterIgnoreMethod) {
if (blockType) {
throw new RuntimeException(after + " not allowed return. " + Arrays.toString(afterParamList));
}
return new DefaultInterceptorDefinition(interceptorClazz, targetInterceptorClazz, interceptorType, CaptureType.BEFORE, beforeMethod, null);
}
if (blockType) {View on GitHub (pinned to 744c3d3075)
Solutions
- Change the after method return type to java.lang.Object
- Cast the Object result at the call site where the replaced value is consumed
- If a concrete return type is truly required, switch the interceptor to a non-RESULT_REPLACE InterceptorType
Example fix
// before
public String after(String target, String result, Object[] args) { ... }
// after
public Object after(String target, Object result, Object[] args) { return result; } Defensive patterns
Strategy: validation
Validate before calling
if (type == InterceptorType.RESULT_REPLACE && !after.getReturnType().equals(Object.class)) throw new IllegalStateException("after must return Object"); Try / catch
try { def = factory.buildInterceptorDefinition(...); } catch (RuntimeException e) { log.error("RESULT_REPLACE after must return Object: {}", e.getMessage()); throw e; } Prevention
- Always declare RESULT_REPLACE after methods as returning java.lang.Object
- Avoid covariant return narrowing in interceptor subclasses
- Add a unit test asserting after.getReturnType() == Object.class for result-replace interceptors
When it happens
Trigger: Registering a RESULT_REPLACE interceptor whose after method returns a narrowed type (covariant override) instead of Object.
Common situations: Writing a result-replacement interceptor that returns the concrete business type for convenience; refactoring an after method from Object to a specific type; Java covariant override semantics silently narrowing the declared return type in a subclass.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- ${after} method not found. ${Arrays.toString(afterParamList)
- source and advice class node must not be null.
- not found interceptorHolderClass, className=
- not found interceptor, className=
- invocation fail, className=
AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07).
Data as JSON: /api/errors/b55bc00dd8f6a668.
Report an issue: GitHub.