pinpoint-apm/pinpoint · error · RuntimeException

${pluginConfig.getPluginJarURLExternalForm()} read fail.${ex

Error message

${pluginConfig.getPluginJarURLExternalForm()} read fail.${ex.getMessage()}

What it means

PlainClassLoaderHandler.readJar() reads all .class entries from the plugin jar via JarReader.read() to build class metadata. If any I/O error occurs while opening/reading the plugin jar, it rethrows it as a RuntimeException prefixed with the plugin jar's URL and the IOException message. This means the agent could not enumerate the plugin jar's class files, so plugin classes cannot be defined into the target classloader.

Source

Thrown at agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/classloading/PlainClassLoaderHandler.java:230

        List<FileBinary> fileBinaryList = readJar();

        Map<String, SimpleClassMetadata> classEntryMap = parse(fileBinaryList);

        for (Map.Entry<String, SimpleClassMetadata> entry : classEntryMap.entrySet()) {
            final SimpleClassMetadata classMetadata = entry.getValue();
            if (meetsRequirement(classMetadata.getClassName(), classLoader)) {
                ClassLoadingChecker classLoadingChecker = new ClassLoadingChecker();
                classLoadingChecker.isFirstLoad(classMetadata.getClassName());
                define0(classLoader, attachment, classMetadata, classEntryMap, classLoadingChecker);
            }
        }
    }

    private List<FileBinary> readJar() {
        try {
            return pluginJarReader.read(ExtensionFilter.CLASS_FILTER);
        } catch (IOException ex) {
            throw new RuntimeException(pluginConfig.getPluginJarURLExternalForm() + " read fail." + ex.getMessage(), ex);
        }
    }

    private Map<String, SimpleClassMetadata> parse(List<FileBinary> fileBinaryList) {
        Map<String, SimpleClassMetadata> parseMap = new HashMap<>();
        for (FileBinary fileBinary : fileBinaryList) {
            SimpleClassMetadata classNode = parseClass(fileBinary);
            parseMap.put(classNode.getClassName(), classNode);
        }
        return parseMap;
    }

    private SimpleClassMetadata parseClass(FileBinary fileBinary) {
        byte[] fileBinaryArray = fileBinary.getFileBinary();
        return SimpleClassMetadataReader.readSimpleClassMetadata(fileBinaryArray);
    }

    private void define0(final ClassLoader classLoader, ClassLoaderAttachment attachment, SimpleClassMetadata currentClass, Map<String, SimpleClassMetadata> classMetaMap, ClassLoadingChecker classLoadingChecker) {

View on GitHub (pinned to 744c3d3075)

Solutions

  1. Verify the plugin jar at the URL printed in the message exists and is a valid jar: run `unzip -t <pluginJarPath>`; re-download/rebuild the plugin if the test fails.
  2. Check file read permissions for the user running the pinpoint agent on the plugin jar and its directory (chmod/chown as needed).
  3. Confirm profiler.plugin.dir (or equivalent plugin config) points to the real plugin directory, not a staging/temp path that has been cleaned up.
  4. Restart the agent after replacing/upgrading plugin jars so PluginConfig references a fresh, stable File instead of a deleted one.
  5. Inspect the wrapped IOException cause for the underlying reason (file not found vs. missing zip END_HEADER) and fix accordingly.

Example fix

# before (invalid/corrupted jar)
ls $AGENT_HOME/plugin
tomcat-instrumentation-0.0.1-SNAPSHOT.jar  # 12 bytes, partial download
# after
mv tomcat-instrumentation-0.0.1-SNAPSHOT.jar /tmp/broken.jar
mv ~/.m2/.../tomcat-instrumentation-0.0.1-SNAPSHOT.jar $AGENT_HOME/plugin/
unzip -t $AGENT_HOME/plugin/tomcat-instrumentation-0.0.1-SNAPSHOT.jar  # 'No errors detected'
Defensive patterns

Strategy: validation

Validate before calling

// before starting the agent, validate every plugin jar
File pluginJar = new File(pluginDir, "my-plugin.jar");
if (!pluginJar.isFile() || pluginJar.length() == 0) throw new IllegalStateException("missing plugin jar");
try (JarFile jf = new JarFile(pluginJar)) {
    if (jf.getEntry("com/myplugin/MyPlugin.class") == null) throw new IllegalStateException("plugin jar incomplete");
} catch (IOException e) { throw new IllegalStateException("corrupted/unreadable plugin jar: " + pluginJar, e); }

Type guard

static boolean isReadableJar(File f) {
    try (JarFile jf = new JarFile(f)) { return f.canRead(); }
    catch (IOException e) { return false; }
}

Try / catch

try {
    injector.injectClass(cl, className);
} catch (RuntimeException e) {
    if (e.getCause() instanceof IOException && e.getMessage().contains("read fail.")) {
        logger.error("plugin jar unreadable: {}", pluginJarPath, e.getCause());
        // fail fast or degrade without this plugin
    } else throw e;
}

Prevention

When it happens

Trigger: Plugin class injection triggers defineJarClass() -> readJar() when a class from the plugin package must be defined into an application classloader; JarReader.read(ExtensionFilter.CLASS_FILTER) throws IOException because the jar is missing, corrupted, unreadable (permissions), deleted while the agent runs, or not a valid zip/jar (e.g. an HTML error page saved as a jar, or a truncated download).

Common situations: Wrong pinpoint plugin dir configured so the 'jar' is a placeholder or partial download; corrupted plugin jar after a failed in-place upgrade while the agent was running; file permissions changed by deployment tooling; disk/network mount issues making the jar unreadable; broken plugin packaging step.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07). Data as JSON: /api/errors/b1fb05df70392bc6. Report an issue: GitHub.