{"record":{"id":"8b6e404ba1077868","repo":"apache/pulsar","slug":"authentication-already-closed","errorCode":null,"errorMessage":"Authentication already closed.","messagePattern":"Authentication already closed\\.","errorType":"exception","errorClass":"PulsarClientException.AlreadyClosedException","httpStatus":null,"severity":"error","filePath":"pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2.java","lineNumber":264,"sourceCode":"\n    @Override\n    public void start() throws PulsarClientException {\n        flow.initialize();\n    }\n\n    /**\n     * The first time that this method is called, it retrieves a token. All subsequent\n     * calls should get a cached value. However, if there is an issue with the Identity\n     * Provider, there is a chance that the background thread responsible for keeping\n     * the refresh token hot will\n     * @return The authentication data identifying this client that will be sent to the broker\n     * @throws PulsarClientException\n     */\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public synchronized AuthenticationDataProvider getAuthData() throws PulsarClientException {\n        if (isClosed) {\n            throw new PulsarClientException.AlreadyClosedException(\"Authentication already closed.\");\n        }\n        if (this.cachedToken == null || this.cachedToken.isExpired()) {\n            this.authenticate();\n        }\n        return this.cachedToken.getAuthData();\n    }\n\n    /**\n     * The IdP TLS material the configured flow carries, folded into a {@link TlsPurpose#CLIENT_OAUTH2}\n     * {@link TlsPolicy} so the framework HTTP client can serve IdP mTLS / custom trust on the new PIP-478 TLS\n     * path. Read at client-build / TLS-compose time (the flow is created during\n     * {@link #configure}, before the client is constructed). Empty when no flow is configured or it carries\n     * no IdP TLS material.\n     *\n     * @return the CLIENT_OAUTH2 policy, or empty\n     */\n    public Optional<TlsPolicy> idpTlsPolicy() {\n        return idpTlsPolicy(null, null);","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2.java#L246-L282","documentation":"getAuthData() is synchronized and checks the isClosed flag; once close() has been called, any attempt to fetch credentials throws AlreadyClosedException (a PulsarClientException subclass). It indicates the Authentication object's lifecycle has ended and it cannot mint or return tokens anymore.","triggerScenarios":"Calling getAuthData() (directly or via currentAccessToken) after authentication.close(); using a cached Authentication instance whose client was shut down; tests reusing a closed auth object.","commonSituations":"Application shutdown racing with in-flight token refresh; sharing one Authentication instance across multiple clients and closing one of them; reconnect logic firing after client teardown.","solutions":["Create a fresh AuthenticationOAuth2 instance instead of reusing a closed one","Guard with authentication state checks before calling getAuthData()","Ensure the client that owns the authentication is not closed while the token is needed"],"exampleFix":"// before\nclient.close();\nAuthenticationDataProvider d = auth.getAuthData(); // throws\n// after\nAuthenticationDataProvider d = auth.getAuthData();\nclient.close();","handlingStrategy":"try-catch","validationCode":"// guard on lifecycle before use\nif (!authLifecycleOpen) {\n    throw new IllegalStateException(\"Authentication closed; recreate before getAuthData()\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    AuthenticationDataProvider data = auth.getAuthData();\n} catch (PulsarClientException.AlreadyClosedException e) {\n    auth = createNewAuthentication(); // recreate and retry once\n    AuthenticationDataProvider data = auth.getAuthData();\n}","preventionTips":["Do not share one Authentication instance across independently closed clients","Close authentication only after all token consumers are done","Track closed state centrally and route getAuthData calls through a lifecycle manager"],"tags":["lifecycle","oauth2","pulsar-client"],"backgroundTag":"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"}