Skip to content

Commit 321d040

Browse files
Vamsi-klucodingrealitylabsgirlcoder-gamingclaude
committed
fix(dagster-aws): handle s3_prefix=None in PickledObjectS3IOManager
boto3's list_objects rejects Prefix=None with a ParamValidationError. Normalize None to empty string before passing to boto3. Fixes: #33490 Co-Authored-By: codingrealitylabs <codingrealitylabs@users.noreply.github.com> Co-Authored-By: girlcoder-gaming <girlcoder-gaming@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f8430dc commit 321d040

2 files changed

Lines changed: 41 additions & 1 deletion

File tree

python_modules/libraries/dagster-aws/dagster_aws/s3/io_manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ def __init__(
3232
self.bucket = check.str_param(s3_bucket, "s3_bucket")
3333
check.opt_str_param(s3_prefix, "s3_prefix")
3434
self.s3 = s3_session
35+
# boto3 rejects Prefix=None; normalize to empty string
36+
s3_prefix = s3_prefix or ""
3537
self.s3.list_objects(Bucket=s3_bucket, Prefix=s3_prefix, MaxKeys=1)
3638
base_path = UPath(s3_prefix) if s3_prefix else None
3739
super().__init__(base_path=base_path)

python_modules/libraries/dagster-aws/dagster_aws_tests/s3_tests/test_io_manager.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
from dagster._core.definitions.source_asset import SourceAsset
2525
from dagster._core.definitions.unresolved_asset_job_definition import define_asset_job
2626

27-
from dagster_aws.s3.io_manager import S3PickleIOManager, s3_pickle_io_manager
27+
from dagster_aws.s3.io_manager import (
28+
PickledObjectS3IOManager,
29+
S3PickleIOManager,
30+
s3_pickle_io_manager,
31+
)
2832
from dagster_aws.s3.utils import construct_s3_client
2933

3034

@@ -112,6 +116,40 @@ def output_prefix_execution_plan():
112116
return output_prefix_execution_plan
113117

114118

119+
@pytest.mark.parametrize("s3_prefix", [None, ""])
120+
def test_pickled_object_s3_io_manager_none_and_empty_prefix(mock_s3_bucket, s3_prefix):
121+
"""Regression test for https://github.com/dagster-io/dagster/issues/33490.
122+
123+
PickledObjectS3IOManager accepts s3_prefix=None in its signature but previously passed None
124+
directly to boto3's list_objects(Prefix=None), which raises ParamValidationError.
125+
126+
Both None and "" should result in no prefix being applied (base_path=None).
127+
"""
128+
import boto3
129+
130+
s3_client = boto3.client("s3", region_name="us-east-1")
131+
# Should not raise botocore.exceptions.ParamValidationError
132+
io_manager = PickledObjectS3IOManager(
133+
s3_bucket=mock_s3_bucket.name,
134+
s3_session=s3_client,
135+
s3_prefix=s3_prefix,
136+
)
137+
assert io_manager.bucket == mock_s3_bucket.name
138+
139+
140+
def test_pickled_object_s3_io_manager_with_valid_prefix(mock_s3_bucket):
141+
"""Verify PickledObjectS3IOManager correctly stores a valid prefix."""
142+
import boto3
143+
144+
s3_client = boto3.client("s3", region_name="us-east-1")
145+
io_manager = PickledObjectS3IOManager(
146+
s3_bucket=mock_s3_bucket.name,
147+
s3_session=s3_client,
148+
s3_prefix="my-prefix",
149+
)
150+
assert io_manager.bucket == mock_s3_bucket.name
151+
152+
115153
def test_s3_pickle_io_manager_prefix(mock_s3_bucket):
116154
assert not len(list(mock_s3_bucket.objects.all()))
117155

0 commit comments

Comments
 (0)