alibaba/canal · error · IllegalStateException

alarmHandlerPluginDir [%s] can't find any name endswith ".ja

Error message

alarmHandlerPluginDir [%s] can't find any name endswith ".jar" file.

What it means

Thrown inside initAlarmHandler() (CanalInstanceWithManager.java:110-112) when both canal.alarm.handler.class and canal.alarm.handler.plugin.dir are configured, but the plugin directory either does not exist or contains no files ending in '.jar'. File.listFiles returns null for a non-existent/non-directory path, or an empty array if the dir has no jars; either triggers IllegalStateException, which is then caught and rethrown as CanalException (see error 345). The check exists because canal dynamically loads the alarm handler from a URLClassLoader built from those jars.

Source

Thrown at instance/manager/src/main/java/com/alibaba/otter/canal/instance/manager/CanalInstanceWithManager.java:111

    public void start() {
        // 初始化metaManager
        logger.info("start CannalInstance for {}-{} with parameters:{}", canalId, destination, parameters);
        super.start();
    }

    @SuppressWarnings("resource")
    protected void initAlarmHandler() {
        logger.info("init alarmHandler begin...");
        String alarmHandlerClass = parameters.getAlarmHandlerClass();
        String alarmHandlerPluginDir = parameters.getAlarmHandlerPluginDir();
        if (alarmHandlerClass == null || alarmHandlerPluginDir == null) {
            alarmHandler = new LogAlarmHandler();
        } else {
            try {
                File externalLibDir = new File(alarmHandlerPluginDir);
                File[] jarFiles = externalLibDir.listFiles((dir, name) -> name.endsWith(".jar"));
                if (jarFiles == null || jarFiles.length == 0) {
                    throw new IllegalStateException(String.format("alarmHandlerPluginDir [%s] can't find any name endswith \".jar\" file.",
                        alarmHandlerPluginDir));
                }
                URL[] urls = new URL[jarFiles.length];
                for (int i = 0; i < jarFiles.length; i++) {
                    urls[i] = jarFiles[i].toURI().toURL();
                }
                ClassLoader currentClassLoader = new URLClassLoader(urls,
                    CanalInstanceWithManager.class.getClassLoader());
                Class<CanalAlarmHandler> _alarmClass = (Class<CanalAlarmHandler>) currentClassLoader.loadClass(alarmHandlerClass);
                alarmHandler = _alarmClass.newInstance();
                logger.info("init [{}] alarm handler success.", alarmHandlerClass);
            } catch (Throwable e) {
                String errorMsg = String.format("init alarmHandlerPluginDir [%s] alarm handler [%s] error: %s",
                    alarmHandlerPluginDir,
                    alarmHandlerClass,
                    ExceptionUtils.getFullStackTrace(e));
                logger.error(errorMsg);
                throw new CanalException(errorMsg, e);

View on GitHub (pinned to 87be50e876)

Solutions

  1. Verify the directory exists and is readable from the canal process's working directory: ls -la <alarmHandlerPluginDir> and confirm at least one *.jar is present (lowercase).
  2. Use an absolute path for canal.alarm.handler.plugin.dir to avoid working-directory ambiguity.
  3. If the plugin dir is optional, remove canal.alarm.handler.class and canal.alarm.handler.plugin.dir to fall back to the default LogAlarmHandler.
  4. On case-sensitive filesystems ensure jars end in lowercase '.jar' (the filter is case-sensitive) or rename '.JAR' -> '.jar'.

Example fix

# before — wrong/relative dir
canal.alarm.handler.class = com.example.MyAlarmHandler
canal.alarm.handler.plugin.dir = plugin

# after — absolute path containing my-alarm.jar
canal.alarm.handler.class = com.example.MyAlarmHandler
canal.alarm.handler.plugin.dir = /opt/canal/plugin
Defensive patterns

Strategy: validation

Validate before calling

String dir = parameters.getAlarmHandlerPluginDir();
String cls = parameters.getAlarmHandlerClass();
if (cls != null && dir != null) {
    File d = new File(dir);
    File[] jars = d.listFiles((f, n) -> n.toLowerCase(Locale.ROOT).endsWith(".jar"));
    if (!d.isDirectory() || jars == null || jars.length == 0) {
        throw new IllegalStateException(
            "alarmHandlerPluginDir " + dir + " is missing or contains no .jar files");
    }
}

Prevention

When it happens

Trigger: Setting parameters alarmHandlerClass and alarmHandlerPluginDir (non-null) and then starting the instance, when the directory path is wrong, missing, not a directory, or contains zero *.jar files. The lambda `name.endsWith(".jar")` is case-sensitive, so `.JAR` files are also excluded.

Common situations: Typo in the plugin dir path; relative path resolved against the wrong working directory (canal launches from deployer/bin, not the conf dir); plugin jar renamed/missing after deploy; case mismatch (.JAR vs .jar on case-sensitive filesystems); directory mounted empty in a container/k8s deployment.

Related errors


AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14). Data as JSON: /api/errors/c06920d82f3768cd. Report an issue: GitHub.