{"record":{"id":"c083b7caf571d63f","repo":"apache/pulsar","slug":"connector-is-already-closed","errorCode":null,"errorMessage":"Connector is already closed","messagePattern":"Connector is already closed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/io/Connector.java","lineNumber":89,"sourceCode":"    }\n\n    public String getArchiveChecksumHex() {\n        return archiveChecksumHex;\n    }\n\n    public synchronized ValidatableFunctionPackage getConnectorFunctionPackage() {\n        checkState();\n        if (connectorFunctionPackage == null) {\n            connectorFunctionPackage =\n                    new FunctionFilePackage(archivePath.toFile(), narExtractionDirectory, enableClassloading,\n                            ConnectorDefinition.class);\n        }\n        return connectorFunctionPackage;\n    }\n\n    private void checkState() {\n        if (closed) {\n            throw new IllegalStateException(\"Connector is already closed\");\n        }\n    }\n\n    public synchronized List<ConfigFieldDefinition> getSourceConfigFieldDefinitions() {\n        checkState();\n        if (sourceConfigFieldDefinitions == null && !StringUtils.isEmpty(connectorDefinition.getSourceClass())\n                && !StringUtils.isEmpty(connectorDefinition.getSourceConfigClass())) {\n            sourceConfigFieldDefinitions = ConnectorUtils.getConnectorConfigDefinition(getConnectorFunctionPackage(),\n                    connectorDefinition.getSourceConfigClass());\n        }\n        return sourceConfigFieldDefinitions;\n    }\n\n    public synchronized List<ConfigFieldDefinition> getSinkConfigFieldDefinitions() {\n        checkState();\n        if (sinkConfigFieldDefinitions == null && !StringUtils.isEmpty(connectorDefinition.getSinkClass())\n                && !StringUtils.isEmpty(connectorDefinition.getSinkConfigClass())) {\n            sinkConfigFieldDefinitions = ConnectorUtils.getConnectorConfigDefinition(getConnectorFunctionPackage(),","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/io/Connector.java#L71-L107","documentation":"Connector wraps a connector (source/sink) NAR package and is closeable; its private checkState() throws this IllegalStateException when any accessor is used after close(). Methods like getConnectorFunctionPackage(), getSourceConfigFieldDefinitions() and getSinkConfigFieldDefinitions() call checkState() first to prevent use of a closed connector archive.","triggerScenarios":"Calling getConnectorFunctionPackage(), getSourceConfigFieldDefinitions(), or getSinkConfigFieldDefinitions() after Connector.close() was invoked — e.g. during connector reload/eviction on the worker while another thread still holds the old Connector instance.","commonSituations":"Reload race: built-in connector NARs reloaded (closing old Connector objects) while validation code still uses a previously obtained Connector; custom tooling caching a Connector and reading config field definitions after closing it; long-lived references surviving an admin-triggered reconfiguration.","solutions":["Obtain a fresh Connector instance from the connector definitions loader rather than using the closed one.","Ensure close() is only called after all readers finish (or synchronize reload with usage).","In custom code, re-open the Connector when isClosed() is true instead of reusing the handle."],"exampleFix":"// before\nConnector connector = loader.loadConnector(def, narDir, false);\n// ... reload closed it\nList<ConfigFieldDefinition> defs = connector.getSinkConfigFieldDefinitions(); // IllegalStateException\n// after\nconnector = loader.loadConnector(def, narDir, false); // re-resolve before use\nList<ConfigFieldDefinition> defs = connector.getSinkConfigFieldDefinitions();","handlingStrategy":"try-catch","validationCode":"if (connector.isClosed()) {\n    connector = connectorLoader.loadConnector(connectorDefinition, narExtractionDirectory, false); // re-resolve\n}","typeGuard":null,"tryCatchPattern":"try {\n    List<ConfigFieldDefinition> defs = connector.getSinkConfigFieldDefinitions();\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"already closed\")) {\n        connector = connectorLoader.loadConnector(connectorDefinition, narExtractionDirectory, false);\n        List<ConfigFieldDefinition> defs = connector.getSinkConfigFieldDefinitions();\n    } else {\n        throw e;\n    }\n}","preventionTips":["Fetch connector config definitions through a loader that always returns a live Connector instance.","Avoid long-lived caching of Connector handles across connector reloads.","Scope Connector lifetime tightly (open, use, close) within a single operation."],"tags":["pulsar-functions","lifecycle","nar","connector","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"}