apache/pulsar · error · IllegalStateException

Connector is already closed

Error message

Connector is already closed

What it means

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.

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/io/Connector.java:89

    }

    public String getArchiveChecksumHex() {
        return archiveChecksumHex;
    }

    public synchronized ValidatableFunctionPackage getConnectorFunctionPackage() {
        checkState();
        if (connectorFunctionPackage == null) {
            connectorFunctionPackage =
                    new FunctionFilePackage(archivePath.toFile(), narExtractionDirectory, enableClassloading,
                            ConnectorDefinition.class);
        }
        return connectorFunctionPackage;
    }

    private void checkState() {
        if (closed) {
            throw new IllegalStateException("Connector is already closed");
        }
    }

    public synchronized List<ConfigFieldDefinition> getSourceConfigFieldDefinitions() {
        checkState();
        if (sourceConfigFieldDefinitions == null && !StringUtils.isEmpty(connectorDefinition.getSourceClass())
                && !StringUtils.isEmpty(connectorDefinition.getSourceConfigClass())) {
            sourceConfigFieldDefinitions = ConnectorUtils.getConnectorConfigDefinition(getConnectorFunctionPackage(),
                    connectorDefinition.getSourceConfigClass());
        }
        return sourceConfigFieldDefinitions;
    }

    public synchronized List<ConfigFieldDefinition> getSinkConfigFieldDefinitions() {
        checkState();
        if (sinkConfigFieldDefinitions == null && !StringUtils.isEmpty(connectorDefinition.getSinkClass())
                && !StringUtils.isEmpty(connectorDefinition.getSinkConfigClass())) {
            sinkConfigFieldDefinitions = ConnectorUtils.getConnectorConfigDefinition(getConnectorFunctionPackage(),

View on GitHub (pinned to 820761864e)

Solutions

  1. Obtain a fresh Connector instance from the connector definitions loader rather than using the closed one.
  2. Ensure close() is only called after all readers finish (or synchronize reload with usage).
  3. In custom code, re-open the Connector when isClosed() is true instead of reusing the handle.

Example fix

// before
Connector connector = loader.loadConnector(def, narDir, false);
// ... reload closed it
List<ConfigFieldDefinition> defs = connector.getSinkConfigFieldDefinitions(); // IllegalStateException
// after
connector = loader.loadConnector(def, narDir, false); // re-resolve before use
List<ConfigFieldDefinition> defs = connector.getSinkConfigFieldDefinitions();
Defensive patterns

Strategy: try-catch

Validate before calling

if (connector.isClosed()) {
    connector = connectorLoader.loadConnector(connectorDefinition, narExtractionDirectory, false); // re-resolve
}

Try / catch

try {
    List<ConfigFieldDefinition> defs = connector.getSinkConfigFieldDefinitions();
} catch (IllegalStateException e) {
    if (e.getMessage().contains("already closed")) {
        connector = connectorLoader.loadConnector(connectorDefinition, narExtractionDirectory, false);
        List<ConfigFieldDefinition> defs = connector.getSinkConfigFieldDefinitions();
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/c083b7caf571d63f. Report an issue: GitHub.