Skip to content

QualityCheckStep fails with ValidationError when baseline_dataset is a pipeline variable #6206

Description

@ddvlamin-searchingpi

PySDK Version

  • PySDK V2 (2.x)
  • PySDK V3 (3.x)

Describe the bug

QualityCheckStep._generate_baseline_job_inputs() creates a ProcessingInput with an incomplete s3_input dict when baseline_dataset is a pipeline variable (e.g. Join, ParameterString). The dict is missing the required s3_data_type field, causing a Pydantic ValidationError.

The bug is in sagemaker-mlops/src/sagemaker/mlops/workflow/quality_check_step.py line 354:

if is_pipeline_variable(baseline_dataset):
    baseline_dataset_input = ProcessingInput(
        input_name=_BASELINE_DATASET_INPUT_NAME,
        s3_input={
            "s3_uri": self.quality_check_config.baseline_dataset,
            "local_path": baseline_dataset_des,
        }
    )

ProcessingS3Input (from sagemaker.core.shapes) requires s3_data_type as a mandatory field with no default. The else branch correctly provides it via _upload_and_convert_to_processing_input(), but the pipeline variable branch does not.

To reproduce

import boto3
from sagemaker.core.helper.session_helper import Session
from sagemaker.core.workflow.functions import Join
from sagemaker.core.workflow.parameters import ParameterString
from sagemaker.core.workflow.pipeline_context import PipelineSession
from sagemaker.mlops.workflow.quality_check_step import DataQualityCheckConfig, QualityCheckStep
from sagemaker.mlops.workflow.check_job_config import CheckJobConfig

pipeline_session = PipelineSession(boto_session=boto3.Session())

param_endpoint_name = ParameterString(name="EndpointName")

# baseline_dataset is a pipeline variable — resolved at execution time
baseline_dataset_uri = Join(
    on="/",
    values=["s3:/", "my-bucket", param_endpoint_name, "baseline/dataset.parquet"],
)

quality_check_config = DataQualityCheckConfig(
    baseline_dataset=baseline_dataset_uri,
    dataset_format={"parquet": {}},
    output_s3_uri="s3://my-bucket/output/",
)

check_job_config = CheckJobConfig(
    role="arn:aws:iam::123456789012:role/SageMakerRole",
    instance_count=1,
    instance_type="ml.m5.xlarge",
    sagemaker_session=pipeline_session,
)

# This raises ValidationError
step = QualityCheckStep(
    name="compute-baseline",
    quality_check_config=quality_check_config,
    check_job_config=check_job_config,
    skip_check=True,
    register_new_baseline=True,
)

Expected behavior

QualityCheckStep should instantiate successfully when baseline_dataset is a pipeline variable. The fix is to include s3_data_type in the dict:

if is_pipeline_variable(baseline_dataset):
    baseline_dataset_input = ProcessingInput(
        input_name=_BASELINE_DATASET_INPUT_NAME,
        s3_input={
            "s3_uri": self.quality_check_config.baseline_dataset,
            "local_path": baseline_dataset_des,
            "s3_data_type": "S3Prefix",  # <-- missing
        }
    )

Screenshots or logs

ValidationError: 1 validation error for ProcessingInput
s3_input.s3_data_type
  Field required [type=missing, input_value={'s3_uri': Join(on='/', v...baseline_dataset_input'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.13/v/missing

System information

  • SageMaker Python SDK version: sagemaker 3.20.0, sagemaker-mlops 1.20.0, sagemaker-core 2.3.0
  • Framework name: N/A (SageMaker Model Monitor)
  • Framework version: N/A
  • Python version: 3.13.5
  • CPU or GPU: CPU
  • Custom Docker image (Y/N): N

Additional context

The bug is present on the latest main branch as well as all released versions of sagemaker-mlops (1.0–1.20.0). It only manifests when baseline_dataset is a pipeline variable (Join, JsonGet, ParameterString, etc.) — static string paths work fine because they take the else branch which uses _upload_and_convert_to_processing_input().

Workaround: patch ProcessingS3Input to make s3_data_type optional before constructing the step:

from sagemaker.core.shapes import ProcessingInput, ProcessingS3Input

ProcessingS3Input.model_fields["s3_data_type"].default = "S3Prefix"
ProcessingS3Input.model_rebuild(force=True)
ProcessingInput.model_rebuild(force=True)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions