fix(bedrock): reject a malformed batch line whatever the upload's MIME type

The upload only converts, and so re-checks, batch files whose MIME type is on the
allowlist. Falling back to the generic object name let a malformed file sent as
application/x-jsonl skip both checks and land in S3 unchanged, so the object-name
step now runs the same strict per-line parse and raises the 400.
This commit is contained in:
ryan-crabbe-berri 2026-10-20 10:59:08 -07:00
parent 5420ca2ed4
commit f0eed8c56b
2 changed files with 26 additions and 12 deletions

View file

@ -600,12 +600,11 @@ class BedrockFilesConfig(BaseAWSLLM, BaseFilesConfig):
## 1. If jsonl, check if there's a model name
file_content: Final = self._get_content_from_openai_file(extracted_file_data_content)
# Split into lines and parse each line as JSON. A file that is not JSONL gets the generic name below; the
# upload's own conversion is what rejects a malformed batch file.
with suppress(json.JSONDecodeError):
openai_jsonl_content: Final = [json.loads(line) for line in file_content.splitlines() if line.strip()]
if len(openai_jsonl_content) > 0:
return self._get_s3_object_name_from_batch_jsonl(openai_jsonl_content)
# Reject a malformed line here rather than falling back: the upload only converts (and so re-checks) files
# whose MIME type is on the batch allowlist, so for any other MIME type this is the only check.
openai_jsonl_content: Final = _parse_batch_jsonl(file_content)
if len(openai_jsonl_content) > 0:
return self._get_s3_object_name_from_batch_jsonl(openai_jsonl_content)
## 2. If not jsonl, store under a server-generated managed object name
filename: Final = extracted_file_data.get("filename")

View file

@ -4387,10 +4387,25 @@ class TestMalformedBatchJsonl:
assert exc_info.value.status_code == 400
assert "line 3" in exc_info.value.message
def test_object_name_inference_still_falls_back_for_a_non_json_file(self):
from litellm.llms.bedrock.files.transformation import BedrockFilesConfig
@pytest.mark.parametrize("content_type", ["application/jsonl", "application/x-jsonl"])
def test_create_file_rejects_a_malformed_line_before_uploading(self, content_type: str):
"""application/x-jsonl is not on the batch MIME allowlist, so the upload skips conversion for it; the line
check must still run so the broken file is rejected instead of landing in S3 unchanged."""
from litellm.llms.bedrock.common_utils import BedrockError
object_name = BedrockFilesConfig().get_object_name(
{"content": b"just some text, not jsonl", "filename": "notes.txt"}, purpose="batch"
)
assert object_name.endswith("notes.txt")
with respx.mock as router:
any_request: Final = router.route()
with pytest.raises(BedrockError) as exc_info:
litellm.create_file(
file=("batch.jsonl", _MALFORMED_BATCH_JSONL, content_type),
purpose="batch",
custom_llm_provider="bedrock",
model="bedrock/amazon.nova-pro-v1:0",
s3_bucket_name="litellm-batch-bucket",
aws_region_name="us-west-2",
aws_access_key_id="test-key-id",
aws_secret_access_key="test-secret",
)
assert exc_info.value.status_code == 400
assert "line 3" in exc_info.value.message
assert not any_request.called