Skip to content

Fix NPE in S3PinotFS credential refresh when initialized with a provided S3Client#18644

Open
noob-se7en wants to merge 1 commit into
apache:masterfrom
noob-se7en:fix/s3pinotfs-init-s3config-npe
Open

Fix NPE in S3PinotFS credential refresh when initialized with a provided S3Client#18644
noob-se7en wants to merge 1 commit into
apache:masterfrom
noob-se7en:fix/s3pinotfs-init-s3config-npe

Conversation

@noob-se7en
Copy link
Copy Markdown
Contributor

Problem

S3PinotFS.init(S3Client, String, PinotConfiguration) (the "bring your own client" overload) sets _s3Client but leaves the _s3Config field null. It constructs an S3Config only as a local variable, used for the server-side-encryption / multipart / ACL setup. The single-arg init(PinotConfiguration) is currently the only method that assigns the _s3Config field.

Any later operation that goes through the credential-refresh path — retryWithS3CredentialRefresh(...)initOrRefreshS3Client() — reads _s3Config.getRegion(). For an instance initialized with a caller-provided client, _s3Config is still null, so the first credential refresh throws a NullPointerException.

Fix

Assign the S3Config that the three-arg init already constructs to the _s3Config field (and reuse it for the existing SSE/multipart/ACL calls). No extra object construction. The field is now populated, so a later refresh rebuilds the client from the same config instead of NPEing.

Testing

Adds S3PinotFSInitTest.testInitWithProvidedClientPopulatesConfigForRefresh: initializes via the three-arg init, then calls initOrRefreshS3Client(). It reproduces the NullPointerException before this change and passes after. Static credentials keep the rebuild fully offline (no S3 mock container required).

…ded S3Client

`S3PinotFS.init(S3Client, String, PinotConfiguration)` set `_s3Client` but left
the `_s3Config` field null — it built an `S3Config` only as a local for the
server-side-encryption / multipart / ACL setup. The single-arg
`init(PinotConfiguration)` is the only path that assigned `_s3Config`.

Any later operation routed through `retryWithS3CredentialRefresh(...)` ->
`initOrRefreshS3Client()` reads `_s3Config.getRegion()`, so an instance created
with a caller-provided client threw a NullPointerException on the first
credential refresh.

Assign the already-constructed `S3Config` to the `_s3Config` field (and reuse it
for the SSE/multipart/ACL setup). No extra object construction; the field is now
populated so a refresh rebuilds the client from the same config instead of
NPEing.

Adds `S3PinotFSInitTest` which initializes via the three-arg `init` and then
calls `initOrRefreshS3Client()` — it reproduces the NPE before this change and
passes after. Static credentials keep the rebuild fully offline (no container).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@noob-se7en noob-se7en added bug Something is not working as expected pinot-filesystem Related to PinotFS abstraction (S3, GCS, ADLS, HDFS) labels Jun 1, 2026
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jun 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 64.42%. Comparing base (a6463d4) to head (0e014e5).
⚠️ Report is 58 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master   #18644      +/-   ##
============================================
+ Coverage     64.27%   64.42%   +0.14%     
- Complexity     1137     1299     +162     
============================================
  Files          3314     3362      +48     
  Lines        204064   207907    +3843     
  Branches      31760    32463     +703     
============================================
+ Hits         131165   133945    +2780     
- Misses        62373    63180     +807     
- Partials      10526    10782     +256     
Flag Coverage Δ
custom-integration1 100.00% <ø> (ø)
integration 100.00% <ø> (ø)
integration1 100.00% <ø> (ø)
integration2 0.00% <ø> (ø)
java-21 64.42% <100.00%> (+0.14%) ⬆️
temurin 64.42% <100.00%> (+0.14%) ⬆️
unittests 64.42% <100.00%> (+0.14%) ⬆️
unittests1 56.79% <ø> (+0.07%) ⬆️
unittests2 37.17% <100.00%> (+1.62%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@xiangfu0 xiangfu0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found one high-signal issue; see inline comment.

// through initOrRefreshS3Client(), which reads _s3Config.getRegion(); leaving the field null
// caused a NullPointerException on the first refresh for instances initialized with a provided
// client.
_s3Config = new S3Config(serverSideEncryptionConfig);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes the init(S3Client, ..., PinotConfiguration) path, but the other public provided-client overload at init(S3Client) still leaves _s3Config null. If a caller uses that overload and later hits the 401/403 retry path, initOrRefreshS3Client() will still dereference _s3Config.getRegion() and fail before the retry happens. Please either initialize _s3Config there as well or explicitly guard/disable credential refresh for bare-client initialization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something is not working as expected pinot-filesystem Related to PinotFS abstraction (S3, GCS, ADLS, HDFS)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants