apache/pulsar · error · IllegalStateException
FunctionArchive is already closed
Error message
FunctionArchive is already closed
What it means
FunctionArchive lazily loads the function package (FunctionFilePackage) from the archive and guards it with a closed flag. getFunctionPackage() throws this IllegalStateException when the archive has been closed (e.g. by reload/eviction logic that closes NAR archives), so the underlying package can no longer be accessed or built from this instance.
Source
Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/functions/FunctionArchive.java:76
} catch (IOException e) {
throw new UncheckedIOException(e);
}
} else {
this.archiveChecksumHex = "";
}
}
public Path getArchivePath() {
return archivePath;
}
public String getArchiveChecksumHex() {
return archiveChecksumHex;
}
public synchronized ValidatableFunctionPackage getFunctionPackage() {
if (closed) {
throw new IllegalStateException("FunctionArchive is already closed");
}
if (functionPackage == null) {
functionPackage = new FunctionFilePackage(archivePath.toFile(), narExtractionDirectory, enableClassloading,
FunctionDefinition.class);
}
return functionPackage;
}
public FunctionDefinition getFunctionDefinition() {
return functionDefinition;
}
@Override
public synchronized void close() throws Exception {
closed = true;
if (functionPackage instanceof AutoCloseable) {
((AutoCloseable) functionPackage).close();
}View on GitHub (pinned to 820761864e)
Solutions
- Re-acquire a fresh FunctionArchive instance from the archive manager/loader instead of using the stale closed one.
- Serialize access so reload (close) cannot happen while another thread uses the archive, or use the latest reference lookup on every use.
- If you hold the archive in custom code, treat close() as terminal and re-open the archive when needed.
Example fix
// before FunctionArchive archive = loader.load(archivePath); // kept as field // ... later, after a reload closed it archive.getFunctionPackage(); // IllegalStateException: FunctionArchive is already closed // after archive = loader.load(archivePath); // reload/re-resolve before use archive.getFunctionPackage();
Defensive patterns
Strategy: try-catch
Validate before calling
if (archive.isClosed()) {
archive = functionArchiveLoader.load(archivePath, narExtractionDirectory, enableClassloading); // re-resolve
} Try / catch
try {
ValidatableFunctionPackage pkg = archive.getFunctionPackage();
} catch (IllegalStateException e) {
if (e.getMessage().contains("already closed")) {
archive = functionArchiveLoader.load(archivePath, narExtractionDirectory, enableClassloading);
ValidatableFunctionPackage pkg = archive.getFunctionPackage();
} else {
throw e;
}
} Prevention
- Don't cache FunctionArchive references across reloads; resolve the current instance on each use.
- Synchronize reload (close) and usage, or use atomic reference swap for the latest archive.
- Treat close() as terminal — never call methods on a closed archive.
When it happens
Trigger: Calling getFunctionPackage() (directly or via isBuiltInFunction, getBuiltinFunctionPackage, validateUpdateRequestParams) after close() was invoked on the FunctionArchive — typically after a NAR reload check decided the archive must be reloaded and closed the old instance, then a caller still holds and uses the stale reference.
Common situations: Concurrency/reload race: thread A reloads a NAR (closing the old FunctionArchive) while thread B holds the old reference and calls getFunctionPackage; caching a FunctionArchive long-term in custom code and using it after the broker/worker reloaded built-in connectors or functions.
Related errors
- Connector is already closed
- The log error handler cannot be changed once the appender is
- javaInstanceRunnable is not initialized
- %s package does not have the correct format. Pulsar cannot d
- Could not find built in archive definition
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/91c0faa963a1f712.
Report an issue: GitHub.