{"record":{"id":"91c0faa963a1f712","repo":"apache/pulsar","slug":"functionarchive-is-already-closed","errorCode":null,"errorMessage":"FunctionArchive is already closed","messagePattern":"FunctionArchive is already closed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/functions/FunctionArchive.java","lineNumber":76,"sourceCode":"            } catch (IOException e) {\n                throw new UncheckedIOException(e);\n            }\n        } else {\n            this.archiveChecksumHex = \"\";\n        }\n    }\n\n    public Path getArchivePath() {\n        return archivePath;\n    }\n\n    public String getArchiveChecksumHex() {\n        return archiveChecksumHex;\n    }\n\n    public synchronized ValidatableFunctionPackage getFunctionPackage() {\n        if (closed) {\n            throw new IllegalStateException(\"FunctionArchive is already closed\");\n        }\n        if (functionPackage == null) {\n            functionPackage = new FunctionFilePackage(archivePath.toFile(), narExtractionDirectory, enableClassloading,\n                    FunctionDefinition.class);\n        }\n        return functionPackage;\n    }\n\n    public FunctionDefinition getFunctionDefinition() {\n        return functionDefinition;\n    }\n\n    @Override\n    public synchronized void close() throws Exception {\n        closed = true;\n        if (functionPackage instanceof AutoCloseable) {\n            ((AutoCloseable) functionPackage).close();\n        }","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/functions/FunctionArchive.java#L58-L94","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nFunctionArchive archive = loader.load(archivePath); // kept as field\n// ... later, after a reload closed it\narchive.getFunctionPackage(); // IllegalStateException: FunctionArchive is already closed\n// after\narchive = loader.load(archivePath); // reload/re-resolve before use\narchive.getFunctionPackage();","handlingStrategy":"try-catch","validationCode":"if (archive.isClosed()) {\n    archive = functionArchiveLoader.load(archivePath, narExtractionDirectory, enableClassloading); // re-resolve\n}","typeGuard":null,"tryCatchPattern":"try {\n    ValidatableFunctionPackage pkg = archive.getFunctionPackage();\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"already closed\")) {\n        archive = functionArchiveLoader.load(archivePath, narExtractionDirectory, enableClassloading);\n        ValidatableFunctionPackage pkg = archive.getFunctionPackage();\n    } else {\n        throw e;\n    }\n}","preventionTips":["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."],"tags":["pulsar-functions","lifecycle","nar","use-after-close"],"backgroundTag":"resource-already-closed","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}