{"record":{"id":"3d2e9ef2ccbedabe","repo":"alibaba/canal","slug":"destination-s-should-start-first","errorCode":null,"errorMessage":"destination:%s should start first","messagePattern":"destination:(.+?) should start first","errorType":"exception","errorClass":"CanalServerException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/com/alibaba/otter/canal/server/embedded/CanalServerWithEmbedded.java","lineNumber":534,"sourceCode":"                }\n            } catch (Exception e) {\n                throw new CanalServerException(e);\n            }\n        }\n    }\n\n    private void checkSubscribe(ClientIdentity clientIdentity) {\n        CanalInstance canalInstance = canalInstances.get(clientIdentity.getDestination());\n        boolean hasSubscribe = canalInstance.getMetaManager().hasSubscribe(clientIdentity);\n        if (!hasSubscribe) {\n            throw new CanalServerException(String.format(\"ClientIdentity:%s should subscribe first\",\n                clientIdentity.toString()));\n        }\n    }\n\n    private void checkStart(String destination) {\n        if (!isStart(destination)) {\n            throw new CanalServerException(String.format(\"destination:%s should start first\", destination));\n        }\n    }\n\n    private void loadCanalMetrics() {\n        ServiceLoader<CanalMetricsProvider> providers = ServiceLoader.load(CanalMetricsProvider.class);\n        List<CanalMetricsProvider> list = new ArrayList<>();\n        for (CanalMetricsProvider provider : providers) {\n            list.add(provider);\n        }\n\n        if (list.isEmpty()) {\n            return;\n        }\n\n        // only allow ONE provider\n        if (list.size() > 1) {\n            logger.warn(\"Found more than one CanalMetricsProvider, use the first one.\");\n            // 报告冲突","sourceCodeStart":516,"sourceCodeEnd":552,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/server/src/main/java/com/alibaba/otter/canal/server/embedded/CanalServerWithEmbedded.java#L516-L552","documentation":"Thrown by the private checkStart() guard, called before checkSubscribe and every batch operation. It calls isStart(destination); if the CanalInstance for that destination is not in the canalInstances map / not started, the call aborts because there is no running pipeline to serve events.","triggerScenarios":"Calling any client-facing method (get/ack/rollback/subscribe) for a destination whose CanalInstance was never started, was stopped, or whose name does not match a configured instance. The server must have called start(destination) or the instance must be auto-started at server startup.","commonSituations":"Destination name typo between canal instance config (canal.properties / instance.properties) and the client; instance failed to start at boot (e.g. DB connection error) leaving it absent; calling client APIs before CanalServerWithEmbedded.start() completes; instance stopped for maintenance.","solutions":["Confirm the destination is listed and successfully started: check server.getCanalInstances() / startup logs for the destination.","Match the destination string exactly to the instance directory name under conf/.","Fix any underlying instance start failure (DB connection, parser error) so the instance registers.","Order startup so CanalServer.start() and per-instance start complete before clients connect."],"exampleFix":"// before: client calls before instance ready\nserver.start(); // returns before instances are up\nserver.subscribe(cid);\n// after: ensure destination started\nserver.start();\nif (!server.isStart(destination)) {\n    throw new IllegalStateException(\"instance not started: \" + destination);\n}\nserver.subscribe(cid);","handlingStrategy":"validation","validationCode":"if (!server.isStart(destination)) {\n    throw new IllegalStateException(\n        \"canal instance not started: \" + destination + \"; check canal.properties and instance config\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    server.subscribe(clientId);\n} catch (CanalServerException e) {\n    if (e.getMessage().contains(\"should start first\")) {\n        throw new IllegalStateException(\"instance not ready, aborting client connect\", e);\n    }\n    throw e;\n}","preventionTips":["Gate client connections behind a readiness check that confirms isStart(destination).","Monitor instance startup logs for the destination before opening clients.","Keep destination strings identical in instance dir name, canal.properties, and client config."],"tags":["embedded-server","lifecycle","startup","configuration"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}