{"record":{"id":"5814344043106d06","repo":"alibaba/spring-cloud-alibaba","slug":"nacosconfigkeyschangelistener-must-be-marked-as-a","errorCode":null,"errorMessage":"NacosConfigKeysChangeListener must be marked as a single parameter with ConfigChangeEvent","messagePattern":"NacosConfigKeysChangeListener must be marked as a single parameter with ConfigChangeEvent","errorType":"validation","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spring-cloud-alibaba-starters/spring-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosAnnotationProcessor.java","lineNumber":267,"sourceCode":"\n\t\t\tgetNacosConfigManager().getConfigService()\n\t\t\t\t\t.addListener(dataId, group, listener);\n\t\t\ttargetListenerMap.put(refreshTargetKey, listener);\n\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprivate void handleMethodNacosConfigKeysChangeListener(NacosConfigKeysListener annotation, String beanName, Object bean,\n\t\t\tMethod method) {\n\t\tString dataId = annotation.dataId();\n\t\tString group = annotation.group();\n\t\ttry {\n\t\t\tClass<?>[] parameterTypes = method.getParameterTypes();\n\t\t\tif (parameterTypes.length != 1 || !ConfigChangeEvent.class.isAssignableFrom(parameterTypes[0])) {\n\t\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\t\"NacosConfigKeysChangeListener must be marked as a single parameter with ConfigChangeEvent\");\n\t\t\t}\n\n\t\t\tString refreshTargetKey = beanName + \"#method#\" + methodSignature(method);\n\t\t\tTargetRefreshable currentTarget = targetListenerMap.get(refreshTargetKey);\n\t\t\tif (currentTarget != null) {\n\t\t\t\tlog.info(\"[Nacos Config] reset {} listener from  {} to {} \", refreshTargetKey,\n\t\t\t\t\t\tcurrentTarget.getTarget(), bean);\n\t\t\t\tcurrentTarget.setTarget(bean);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlog.info(\"[Nacos Config] register {} listener on {} \", refreshTargetKey,\n\t\t\t\t\tbean);\n\t\t\t// annotation on string.\n\t\t\tNacosPropertiesKeyListener nacosPropertiesKeyListener = new NacosPropertiesKeyListener(bean, wrapArrayToSet(annotation.interestedKeys()),\n\t\t\t\t\twrapArrayToSet(annotation.interestedKeyPrefixes())) {\n","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/alibaba/spring-cloud-alibaba/blob/115d5901102009492e05d5ec18c3f79cad4077d0/spring-cloud-alibaba-starters/spring-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosAnnotationProcessor.java#L249-L285","documentation":"Thrown by NacosAnnotationProcessor.handleMethodNacosConfigKeysChangeListener when a method annotated with @NacosConfigKeysChangeListener does not have exactly one parameter, or that parameter is not assignable to com.alibaba.nacos.api.config.listener.ConfigChangeEvent. The processor inspects method.getParameterTypes() at bean-post-processing time and rejects mis-shaped listener methods with a RuntimeException. This is a usage-contract violation, not a runtime data error.","triggerScenarios":"Annotating a no-arg method, a multi-arg method, or a method whose single argument is not ConfigChangeEvent with @NacosConfigKeysChangeListener. The error surfaces during Spring context refresh when the annotation processor scans beans.","commonSituations":"Copying an example but giving the listener a different signature (e.g., a String or a custom DTO); refactoring a method and dropping the event parameter; misunderstanding the required signature from docs.","solutions":["Give the annotated method exactly one parameter of type ConfigChangeEvent: void onChange(ConfigChangeEvent event).","Remove @NacosConfigKeysChangeListener from methods that cannot match the contract.","Rebuild/restart so the processor re-validates signatures after the fix."],"exampleFix":"// before\n@NacosConfigKeysChangeListener(dataId = \"app.yml\")\npublic void onChange(String content) { ... }\n// after\n@NacosConfigKeysChangeListener(dataId = \"app.yml\")\npublic void onChange(ConfigChangeEvent event) { ... }","handlingStrategy":"validation","validationCode":"// At startup, assert every @NacosConfigKeysChangeListener method has one ConfigChangeEvent param.\nfor (Method m : bean.getClass().getMethods()) {\n    if (m.isAnnotationPresent(NacosConfigKeysChangeListener.class)) {\n        Class<?>[] p = m.getParameterTypes();\n        if (p.length != 1 || !ConfigChangeEvent.class.isAssignableFrom(p[0])) {\n            throw new IllegalStateException(\"bad listener signature: \" + m);\n        }\n    }\n}","typeGuard":"static boolean isKeysChangeListenerShape(java.lang.reflect.Method m) {\n    Class<?>[] p = m.getParameterTypes();\n    return p.length == 1 && ConfigChangeEvent.class.isAssignableFrom(p[0]);\n}","tryCatchPattern":null,"preventionTips":["Always type the listener argument as ConfigChangeEvent.","Keep listener methods single-arg.","Add a startup assertion so contract drift fails fast."],"tags":["nacos","config","annotation","reflection","contract-violation"],"backgroundTag":null,"analyzedSha":"115d5901102009492e05d5ec18c3f79cad4077d0","analyzedAt":"2026-08-14T04:47:13.900Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}