You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@nifi.apache.org by "Lewis J (Jira)" <ji...@apache.org> on 2026/09/19 11:14:00 UTC

[jira] [Created] (NIFI-16369) RESTIcebergCatalog - CredentialsRefreshRESTClient throws UnsupportedOperationException on REST catalog table loads

Lewis J created NIFI-16369:
------------------------------

             Summary: RESTIcebergCatalog - CredentialsRefreshRESTClient throws UnsupportedOperationException on REST catalog table loads
                 Key: NIFI-16369
                 URL: https://issues.apache.org/jira/browse/NIFI-16369
             Project: Apache NiFi
          Issue Type: Bug
          Components: Extensions
    Affects Versions: 2.12.0, 2.13.0
         Environment: nifi-iceberg-rest-catalog
            Reporter: Lewis J


{panel}
When using {{PutIcebergRecord}} with a REST catalog configured for OAuth 2.0 Client Credentials ({{{}rest.auth.type=oauth2{}}}), table load operations fail unconditionally with an {{{}UnsupportedOperationException{}}}. The error occurs because {{{}CredentialsRefreshRESTClient{}}}—which is only active when OAuth 2.0 client credentials are used—fails to forward interface default methods added to Iceberg's {{RESTClient}} interface.

_(Note: Catalog configurations using Bearer token authentication are unaffected, as {{CredentialsRefreshRESTClient}} is not installed for Bearer auth.)_

 
h4. *Steps to Reproduce*
 # 
Configure {{PutIcebergRecord}} to connect to an Iceberg REST catalog using the OAuth 2.0 client-credentials strategy ({{{}rest.auth.type=oauth2{}}} with Client ID and Client Secret).
 # 
Trigger the processor to execute a write or load table operation.

h4. *Expected Result*
{{PutIcebergRecord}} successfully loads table metadata from the REST catalog and processes incoming records.

h4. *Actual Result*
The processor throws an {{UnsupportedOperationException}} on every execution trigger:

```java
{{java.lang.UnsupportedOperationException: Returning response headers is not supported
    at org.apache.iceberg.rest.RESTClient.get(RESTClient.java:131)
    at org.apache.iceberg.rest.RESTSessionCatalog.loadInternal(RESTSessionCatalog.java:438)
    at org.apache.nifi.processors.iceberg.PutIcebergRecord.getTable(PutIcebergRecord.java:223)}}
``` 
No configuration workaround avoids this when using OAuth 2.0 Client Credentials, as {{RESTSessionCatalog.loadInternal}} calls the header-returning {{get(...)}} overload unconditionally during table loads. Swapping to Bearer token authentication works as a temporary workaround because it bypasses the decorator entirely.

h4. *Root Cause Analysis*
{{CredentialsRefreshRESTClient}} is installed only when OAuth 2.0 client credentials are configured. Its sole purpose is to wrap {{RESTClient}} to intercept {{postForm}} calls, rewriting RFC 8693 token exchange requests into standard client-credentials grants.

Upstream Iceberg's {{RESTClient}} interface defines opt-in default methods for handling response headers, query parameters, and parser contexts. By design, the default interface implementations throw {{UnsupportedOperationException}} unless overridden by an implementing transport client.

Because {{CredentialsRefreshRESTClient}} directly implements {{RESTClient}} but only overrides the base abstract methods, invocations of these interface default methods silently bypass the wrapped delegate ({{{}HTTPClient{}}}) and execute the throwing default methods on the interface.

The following five {{RESTClient}} default methods are unhandled and throw when invoked through the decorator:
 * 
{{get(String, Map, Class<T>, Map, Consumer<ErrorResponse>, Consumer<Map<String,String>>)}} _(causes the table load failure)_
 * 
{{get(String, Map, Class<T>, Map, Consumer<ErrorResponse>, ParserContext)}}
 * 
{{post(String, RESTRequest, Class<T>, Map, Consumer<ErrorResponse>, Consumer<Map<String,String>>)}}
 * 
{{post(String, RESTRequest, Class<T>, Map, Consumer<ErrorResponse>, Consumer<Map<String,String>>, ParserContext)}}
 * 
{{delete(String, Map, Class<T>, Map, Consumer<ErrorResponse>)}}

h4. *Proposed Solutions*
Override and forward all five missing {{RESTClient}} default methods in {{CredentialsRefreshRESTClient}} directly to the underlying {{restClient}} delegate. Ensure {{withAuthSession}} continues to return a wrapped decorator instance so session-bound operations maintain the token refresh behavior.

h5. *or possibly...* 
Eliminate the {{CredentialsRefreshRESTClient}} decorator by connecting {{RESTIcebergCatalog}} directly to NiFi's standard {{OAuth2AccessTokenProvider}} Controller Service via Iceberg's {{AuthManager}} SPI.
*if the former...*


h4. *Regression Testing / Verification*
Add a unit test in {{nifi-iceberg-rest-catalog}} asserting that all declared methods on {{RESTClient.class}} are explicitly overridden by {{{}CredentialsRefreshRESTClient{}}}:
```java

{{@Testvoid testAllRESTClientMethodsOverridden() {    for (Method method : RESTClient.class.getMethods()) {
        assertTrue(
            Arrays.stream(CredentialsRefreshRESTClient.class.getDeclaredMethods())
                  .anyMatch(m -> m.getName().equals(method.getName()) && Arrays.equals(m.getParameterTypes(), method.getParameterTypes())),            "CredentialsRefreshRESTClient must explicitly forward RESTClient method: " + method
        );
    }
}}}
```{panel}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)