From 56effbc4681e01d6a4c211af5f39a527c58d6921 Mon Sep 17 00:00:00 2001 From: Lucas Jia Date: Mon, 27 Jul 2026 14:18:40 -0700 Subject: [PATCH] fix(sagemaker-core): drop tags before rebuilding TransformJob in transform() Transformer.transform() submitted the CreateTransformJob request successfully, then rebuilt a local TransformJob resource from the same request dict. That dict contains a "tags" key (Tags is a member of the CreateTransformJobRequest shape), but the TransformJob resource model has no tags field and sets extra="forbid", so the constructor raised a pydantic ValidationError after the job was already created on SageMaker, leaving the caller without a job handle. Pop "tags" from the transformed dict before constructing TransformJob, mirroring the existing ProcessingJob fix (#5459). Add a regression test. --- .../src/sagemaker/core/transformer.py | 2 + sagemaker-core/tests/unit/test_transformer.py | 45 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/sagemaker-core/src/sagemaker/core/transformer.py b/sagemaker-core/src/sagemaker/core/transformer.py index 93a35dc075..efc1db82a5 100644 --- a/sagemaker-core/src/sagemaker/core/transformer.py +++ b/sagemaker-core/src/sagemaker/core/transformer.py @@ -409,6 +409,8 @@ def submit(request): from sagemaker.core.utils.code_injection.codec import transform as transform_util transformed = transform_util(serialized_request, "CreateTransformJobRequest") + # Remove tags from transformed dict as TransformJob resource doesn't accept it + transformed.pop("tags", None) self.latest_transform_job = TransformJob(**transformed) if wait: diff --git a/sagemaker-core/tests/unit/test_transformer.py b/sagemaker-core/tests/unit/test_transformer.py index 621df013f1..6a8fa4cf83 100644 --- a/sagemaker-core/tests/unit/test_transformer.py +++ b/sagemaker-core/tests/unit/test_transformer.py @@ -639,3 +639,48 @@ def test_load_config(self, mock_session): assert "resource_config" in config assert config["output_config"]["s3_output_path"] == "s3://bucket/output" assert config["resource_config"]["instance_count"] == 2 + + +class TestTransformerTransformWithTags: + """Regression tests for tags handling in Transformer.transform() (issue #6090).""" + + def test_transform_removes_tags_before_building_transform_job(self, mock_session): + """Tags must be dropped from the transformed dict before TransformJob(**transformed). + + TransformJob resource model has no tags field and sets extra="forbid", so leaving + tags in the dict raises a pydantic ValidationError after the job is already submitted. + """ + transformer = Transformer( + model_name="test-model", + instance_count=1, + instance_type="ml.m5.xlarge", + output_path="s3://bucket/output", + tags=[{"Key": "team", "Value": "ml"}], + sagemaker_session=mock_session, + ) + + # Session is not a PipelineSession, so intercept just invokes submit(request). + mock_session._intercept_create_request = Mock( + side_effect=lambda request, submit, *args, **kwargs: submit(request) + ) + + with patch( + "sagemaker.core.utils.code_injection.codec.transform", + return_value={ + "transform_job_name": "test-job", + "tags": [{"Key": "team", "Value": "ml"}], + }, + ): + with patch("sagemaker.core.transformer.TransformJob") as mock_transform_job_class: + transformer.transform( + data="s3://bucket/input", + content_type="text/csv", + job_name="test-job", + wait=False, + ) + + # TransformJob must be constructed without the tags key. + mock_transform_job_class.assert_called_once() + call_kwargs = mock_transform_job_class.call_args[1] + assert "tags" not in call_kwargs + assert call_kwargs["transform_job_name"] == "test-job"