{"record":{"id":"0f4e864cdad64714","repo":"pinpoint-apm/pinpoint","slug":"after-method-not-found-arrays-tostring-after","errorCode":null,"errorMessage":"${after} method not found. ${Arrays.toString(afterParamList)}","messagePattern":"(.+?) method not found\\. (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InterceptorDefinitionFactory.java","lineNumber":188,"sourceCode":"            if(!this.interceptorClazz.isAssignableFrom(targetClazz)) {\n                return null;\n            }\n            @SuppressWarnings(\"unchecked\")\n            final Class<? extends Interceptor> casting = (Class<? extends Interceptor>) targetClazz;\n            return createInterceptorDefinition(casting);\n        }\n\n        private InterceptorDefinition createInterceptorDefinition(Class<? extends Interceptor> targetInterceptorClazz) {\n\n            final Method beforeMethod = searchMethod(targetInterceptorClazz, before, beforeParamList);\n            if (beforeMethod == null) {\n                throw new RuntimeException(before + \" method not found. \" + Arrays.toString(beforeParamList));\n            }\n            final boolean beforeIgnoreMethod = beforeMethod.isAnnotationPresent(IgnoreMethod.class);\n            final boolean blockType = beforeMethod.getReturnType() == TraceBlock.class;\n            final Method afterMethod = searchMethod(targetInterceptorClazz, after, afterParamList);\n            if (afterMethod == null) {\n                throw new RuntimeException(after + \" method not found. \" + Arrays.toString(afterParamList));\n            }\n            final boolean afterIgnoreMethod = afterMethod.isAnnotationPresent(IgnoreMethod.class);\n\n            if (interceptorType == InterceptorType.RESULT_REPLACE && afterMethod.getReturnType() != Object.class) {\n                // a covariant override would change the weaved call descriptor and break the INVOKEINTERFACE site.\n                throw new RuntimeException(after + \" must return java.lang.Object. \" + targetInterceptorClazz.getName());\n            }\n\n            if (beforeIgnoreMethod && afterIgnoreMethod) {\n                return new DefaultInterceptorDefinition(interceptorClazz, targetInterceptorClazz, interceptorType, CaptureType.NON, null, null);\n            }\n            if (beforeIgnoreMethod) {\n                if (blockType) {\n                    throw new RuntimeException(before + \" not allowed return. \" + Arrays.toString(beforeParamList));\n                }\n                return new DefaultInterceptorDefinition(interceptorClazz, targetInterceptorClazz, interceptorType, CaptureType.AFTER, null, afterMethod);\n            }\n            if (afterIgnoreMethod) {","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InterceptorDefinitionFactory.java#L170-L206","documentation":"InterceptorDefinitionFactory.buildInterceptorDefinition validates the @Interceptee before/after methods declared on an interceptor class. When the configured 'after' method name cannot be found on the target interceptor class with the expected parameter list, it throws this RuntimeException. This happens because the weave-time contract requires both before and after methods to resolve to real Methods before bytecode instrumentation is generated.","triggerScenarios":"Calling createInterceptorDefinition (via InterceptorDefinitionFactory) with an interceptor class whose @Interceptee 'after' value names a method that does not exist on the class, or whose parameter types do not match afterParamList (e.g. wrong signature, renamed method, wrong parameter order).","commonSituations":"Typos in the after method name; copying an interceptor and renaming methods without updating the annotation; signature drift after adding a parameter to the interceptor constructor/after method; writing a custom interceptor whose after signature does not match the chosen InterceptorType.","solutions":["Check the after method name in the @Interceptee annotation matches an actual method on the interceptor class exactly","Compare the after method signature with the required parameter list printed in the error message and fix parameter types/order","If the method is inherited, verify it is declared on the targetInterceptorClazz hierarchy that searchMethod can reach (public/protected, correct class)","Rebuild/redeploy the agent plugin so the compiled interceptor matches the annotation"],"exampleFix":"// before\n@Interceptee(before=\"before\", after=\"aftr\")\nclass MyInterceptor { public void before(...) {} public void after(...) {} }\n// after\n@Interceptee(before=\"before\", after=\"after\")\nclass MyInterceptor { public void before(...) {} public void after(...) {} }","handlingStrategy":"validation","validationCode":"Method m = Arrays.stream(interceptorClazz.getMethods()).filter(x -> x.getName().equals(after) && Arrays.equals(x.getParameterTypes(), paramTypes)).findFirst().orElse(null);\nif (m == null) throw new IllegalStateException(\"after method missing: \" + after);","typeGuard":null,"tryCatchPattern":"try { def = factory.buildInterceptorDefinition(...); } catch (RuntimeException e) { log.error(\"bad interceptor def: {}\", e.getMessage()); throw e; }","preventionTips":["Keep @Interceptee names in sync with actual method names via unit tests that build every interceptor definition","Match after signatures to the InterceptorType contract exactly","Run interceptor definition tests in CI before packaging plugins"],"tags":["java","instrumentation","interceptor","reflection","method-not-found"],"backgroundTag":"method-not-found","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}