diff --git a/sagemaker-train/src/sagemaker/train/model_trainer.py b/sagemaker-train/src/sagemaker/train/model_trainer.py index a3d7748c74..a8d49eb277 100644 --- a/sagemaker-train/src/sagemaker/train/model_trainer.py +++ b/sagemaker-train/src/sagemaker/train/model_trainer.py @@ -1273,7 +1273,9 @@ def _prepare_train_script( execute_driver=execute_driver, ) - with open(os.path.join(tmp_dir.name, TRAIN_SCRIPT), "w") as f: + # The container runs Linux, so the script must be LF-only regardless of the + # host that generated it. + with open(os.path.join(tmp_dir.name, TRAIN_SCRIPT), "w", newline="\n") as f: f.write(train_script) @classmethod diff --git a/sagemaker-train/tests/unit/train/test_model_trainer.py b/sagemaker-train/tests/unit/train/test_model_trainer.py index 31c8abd36a..4d39d26955 100644 --- a/sagemaker-train/tests/unit/train/test_model_trainer.py +++ b/sagemaker-train/tests/unit/train/test_model_trainer.py @@ -2022,6 +2022,26 @@ def test_networking_intelligent_defaults_fills_subnets_on_existing(model_trainer assert model_trainer.networking.security_group_ids == ["sg-preexisting"] +def test_prepare_train_script_writes_lf_line_endings(model_trainer): + """sm_train.sh must use LF endings even when written on a CRLF-default host (Windows). + + The generated script is always executed inside a Linux training container, so a + host that maps text-mode "\n" to "\r\n" (Windows) must not leak CRLF into it - + bash rejects a script whose first line is "\r" (see aws/sagemaker-python-sdk#5904). + """ + with tempfile.TemporaryDirectory() as tmp_dir_name: + + class _FakeTmpDir: + name = tmp_dir_name + + model_trainer._prepare_train_script(_FakeTmpDir(), DEFAULT_SOURCE_CODE) + + script_path = os.path.join(tmp_dir_name, TRAIN_SCRIPT) + with open(script_path, "rb") as f: + raw = f.read() + + assert b"\r\n" not in raw + assert raw.startswith(b"\n#!/bin/bash\n") or raw.startswith(b"#!/bin/bash\n") # Actionable error guidance in train(). The original exception must always propagate # unchanged; the SDK only adds remediation logging for common terminal failures # (quota exhaustion, missing region/credentials) so users and automation stop