{"record":{"id":"ec67b4b1709ce273","repo":"alibaba/canal","slug":"instance-s-groupid-s-load-outeradapters-is-empty","errorCode":null,"errorMessage":"instance=%s,groupId=%s Load OuterAdapters is Empty，pls check rdb.yml","messagePattern":"instance=(.+?),groupId=(.+?) Load OuterAdapters is Empty，pls check rdb\\.yml","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"client-adapter/launcher/src/main/java/com/alibaba/otter/canal/adapter/launcher/loader/CanalAdapterLoader.java","lineNumber":79,"sourceCode":"                    // 保证一定有key\r\n                    if (StringUtils.isEmpty(config.getKey())) {\r\n                        String key = StringUtils.join(\r\n                            new String[] { Util.AUTO_GENERATED_PREFIX, canalAdapter.getInstance(), group.getGroupId(),\r\n                                           String.valueOf(autoGenId) },\r\n                            '-');\r\n                        //gen keyId\r\n                        config.setKey(key);\r\n                    }\r\n                    autoGenId++;\r\n                    loadAdapter(config, canalOuterAdapters);\r\n                }\r\n                canalOuterAdapterGroups.add(canalOuterAdapters);\r\n                // canalOuterAdapters 存在初始化失败的情况，导致canalOuterAdapters的数量，可能小于group.getOuterAdapters\r\n                // 由于group下的 所有OuterAdapter实例都会重复消费同一批消息，因此不允许部分adapter初始化成功，必须全部初始化成功才允许消费\r\n                if(CollectionUtils.isEmpty(canalOuterAdapters) || canalOuterAdapters.size() != group.getOuterAdapters().size() ){\r\n                    String msg = String.format(\"instance=%s,groupId=%s Load OuterAdapters is Empty，pls check rdb.yml\",\r\n                                canalAdapter.getInstance(),group.getGroupId());\r\n                        throw new RuntimeException(msg);\r\n                 }\r\n                AdapterProcessor adapterProcessor = canalAdapterProcessors.computeIfAbsent(\r\n                    canalAdapter.getInstance() + \"|\" + StringUtils.trimToEmpty(group.getGroupId()),\r\n                    f -> new AdapterProcessor(canalClientConfig,\r\n                        canalAdapter.getInstance(),\r\n                        group.getGroupId(),\r\n                        canalOuterAdapterGroups));\r\n                adapterProcessor.start();\r\n                logger.info(\"Start adapter for canal-client mq topic: {} succeed\",\r\n                    canalAdapter.getInstance() + \"-\" + group.getGroupId());\r\n            }\r\n        }\r\n    }\r\n\r\n    private void loadAdapter(OuterAdapterConfig config, List<OuterAdapter> canalOutConnectors) {\r\n        try {\r\n            OuterAdapter adapter;\r\n            adapter = new ProxyOuterAdapter(loader.getExtension(config.getName(), config.getKey()));\r","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/client-adapter/launcher/src/main/java/com/alibaba/otter/canal/adapter/launcher/loader/CanalAdapterLoader.java#L61-L97","documentation":"Thrown by CanalAdapterLoader.init() when, after attempting to load every OuterAdapter in a group, the successfully-initialized list is empty or its size differs from group.getOuterAdapters().size(). Because all adapters in a group consume the same messages, partial init is disallowed: if any one adapter failed to initialize, the whole group is rejected. The message hardcodes 'pls check rdb.yml' but the failure can be any adapter type.","triggerScenarios":"loadAdapter() is called for each config in a group; one or more returns null or throws and is not added to canalOuterAdapters, so its final size is less than group.getOuterAdapters().size(). This commonly happens when an adapter's own init() throws (e.g. errors 160/166/174 'No ... adapter found', bad JDBC properties, missing SPI).","commonSituations":"An rdb/kudu/phoenix/es adapter in the group failed its init() due to a config or connectivity error, and loadAdapter swallowed it, shrinking the list. The wrong yml is often the real cause, even though the message only names rdb.yml.","solutions":["Read the adapter init errors logged just before this one (e.g. 'No rdb adapter found for config key', JDBC failures, 'No kudu adapter found') and fix the first failing adapter.","Verify every outerAdapter entry in the group has correct name/key/properties and that its mapping yml files load and validate.","Confirm the adapter SPI jar is on the classpath (a missing extension makes loadAdapter return null).","Once all adapters in the group init cleanly, the size check passes."],"exampleFix":"# before: second adapter fails init, group rejected\nouterAdapters:\n  - name: logger\n  - name: rdb\n    key: mysql1\n    properties:\n      jdbc.url: jdbc:bogus        # init fails -> count mismatch\n# after\n      jdbc.url: jdbc:mysql://127.0.0.1:3306/mytest2","handlingStrategy":"validation","validationCode":"// After building a group, check the size contract before the loader does.\nList<OuterAdapter> loaded = group.getOuterAdapters().stream()\n    .map(this::tryLoadAdapter) // returns null on failure, logs cause\n    .collect(Collectors.toList());\nlong failures = loaded.stream().filter(Objects::isNull).count();\nif (failures > 0) {\n    throw new IllegalStateException(\n        \"Group \" + group.getGroupId() + \" has \" + failures\n        + \" adapter(s) that failed init; fix those before starting the group\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Configure each adapter in a group and verify it initializes in isolation before combining them.","When this error fires, read the preceding adapter-specific init errors - the named 'rdb.yml' is just the default hint.","Ensure every adapter SPI jar is on the classpath so loadAdapter never returns null."],"tags":["launcher","adapter-init","canal","group-loading"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}