apache/pulsar · warning · IOException
cause.getMessage()
Error message
cause.getMessage()
What it means
rethrowIOException normalizes a Throwable thrown while loading an additional servlet: IOException, RuntimeException, and Error are rethrown as-is; anything else is wrapped in a new IOException whose message is cause.getMessage(). Developers see this as an IOException during servlet (NAR) loading whose text is the underlying cause's message.
Source
Thrown at pulsar-broker-common/src/main/java/org/apache/pulsar/broker/web/plugin/servlet/AdditionalServletUtils.java:204
}
}
default -> throw new IllegalArgumentException("Unsupported additional servlet type " + servletType);
}
throw new IllegalArgumentException("Additional servlet instance of type "
+ (servletInstance == null ? "null" : servletInstance.getClass().getName())
+ " doesn't implement the servlet interface required by the declared servlet type " + servletType);
}
private void rethrowIOException(Throwable cause)
throws IOException {
if (cause instanceof IOException) {
throw (IOException) cause;
} else if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
throw new IOException(cause.getMessage(), cause);
}
}
}
View on GitHub (pinned to 820761864e)
Solutions
- Inspect the IOException's message and its cause stack trace to find the underlying loading failure
- Rebuild/redeploy the servlet NAR — the cause is almost always in the plugin package
- Enable debug logging for the additional servlet loading path to capture the original throwable
Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure the NAR file exists and is readable before load java.io.File nar = new java.io.File(narPath); if (!nar.isFile() || !nar.canRead()) throw new java.io.FileNotFoundException(narPath);
Try / catch
try {
servlet = AdditionalServletUtils.load(definition, narDir);
} catch (IOException e) {
// message is cause.getMessage(); inspect cause chain for the real failure
Throwable root = e; while (root.getCause() != null) root = root.getCause();
log.error("Servlet load failed, root cause: {}", root.toString(), e);
} Prevention
- Always log the full cause chain — the IOException message is only the root message
- Verify NAR integrity (unzip -t) before deploying
- Keep plugin initialization code free of exotic checked exceptions
When it happens
Trigger: AdditionalServletUtils.load encounters a checked, non-IOException, non-RuntimeException, non-Error Throwable during servlet class loading/instantiation (e.g. a custom checked exception from plugin static initializers or class definition failures surfaced as a checked type).
Common situations: Corrupt or incompatible NAR files whose classloading throws unusual checked exceptions; plugin code throwing custom checked exceptions during initialization; unusual filesystem errors surfaced through non-standard exception types.
Related errors
- Failed to load the additional servlet for name `${servletNam
- Additional servlets `${name}` does NOT provide an additional
- Class ${className} does not implement additional servlet int
- Unsupported additional servlet type ${servletType}
- Additional servlet instance of type ${className} doesn't imp
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/447224070495a4a4.
Report an issue: GitHub.