{"record":{"id":"3b8b34cdaca4e5ab","repo":"BerriAI/litellm","slug":"purpose-is-required","errorCode":null,"errorMessage":"purpose is required","messagePattern":"purpose is required","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/bedrock/files/transformation.py","lineNumber":347,"sourceCode":"        Get the complete S3 URL for the file upload request\n        \"\"\"\n        request_params: Final = merge_bedrock_aws_request_params(litellm_params, optional_params)\n        bucket_name = litellm_params.get(\"s3_bucket_name\") or os.getenv(\"AWS_S3_BUCKET_NAME\")\n        if not bucket_name:\n            raise ValueError(\n                \"S3 bucket_name is required. Set 's3_bucket_name' in litellm_params or AWS_S3_BUCKET_NAME env var\"\n            )\n        bucket_name, object_prefix = split_configured_cloud_bucket_name(bucket_name)\n\n        s3_region_name: Final = litellm_params.get(\"s3_region_name\") or optional_params.get(\"s3_region_name\")\n        aws_region_name: Final = s3_region_name or self._get_aws_region_name(request_params, model)\n\n        file_data: Final = data.get(\"file\")\n        purpose: Final = data.get(\"purpose\")\n        if file_data is None:\n            raise ValueError(\"file is required\")\n        if purpose is None:\n            raise ValueError(\"purpose is required\")\n        extracted_file_data: Final = extract_file_data(file_data)\n        object_name = self.get_object_name(extracted_file_data, purpose)\n        if object_prefix:\n            object_name = f\"{object_prefix}/{object_name}\"\n        encoded_object_name: Final = encode_s3_object_key_for_url(object_name)\n\n        # S3 endpoint URL format\n        s3_endpoint_url: Final = (\n            request_params.get(\"s3_endpoint_url\") or f\"https://s3.{aws_region_name}.amazonaws.com\"\n        ).rstrip(\"/\")\n\n        return f\"{s3_endpoint_url}/{bucket_name}/{encoded_object_name}\"\n\n    def get_supported_openai_params(self, model: str) -> list[OpenAICreateFileRequestOptionalParams]:\n        return []\n\n    def map_openai_params(\n        self,","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/bedrock/files/transformation.py#L329-L365","documentation":"The Bedrock create-file handler requires a non-None 'purpose' field alongside 'file'; it determines the S3 object naming strategy (batch JSONL parsing vs generic managed name). data.get('purpose') returned None.","triggerScenarios":"Multipart upload with the file part but the 'purpose' form field omitted, or sent under a different field name (e.g. 'purposes') or inside JSON where form parsing ignores it.","commonSituations":"Clients forwarding only the file; form field name typos; OpenAI-compatible clients that assume purpose is optional.","solutions":["Include purpose='batch' (or the supported value) as a form field in the multipart request.","Verify field name casing/spelling is exactly 'purpose'.","For batch use, ensure the file is valid JSONL so naming proceeds."],"exampleFix":"# before\nrequests.post(url, files={\"file\": f})\n\n# after\nrequests.post(url, files={\"file\": f}, data={\"purpose\": \"batch\"})","handlingStrategy":"validation","validationCode":"assert data.get(\"purpose\") is not None, \"form field 'purpose' is required\"","typeGuard":null,"tryCatchPattern":"try:\n    resp = litellm.create_file(file=f, purpose=\"batch\")\nexcept ValueError as e:\n    if \"purpose is required\" in str(e):\n        return HTTP 400\n    raise","preventionTips":["Send purpose together with the file part in the same multipart request.","Validate the client form schema once, centrally, not ad hoc per endpoint."],"tags":["bedrock","files","upload","validation"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}