Cannot upload PDF file to OneDrive, unclear API Error
Describe the bug
I am trying to upload a PDF file to OneDrive, but I receive a 400 APIError with an Invalid Request.
Expected behavior
Successful upload of the file.
How to reproduce
files = [item.name for item in await self._get_onedrive_items(
query_filter="file ne null and file/mimeType eq 'application/pdf'"
)]
folder_id_map = await self._get_folder_ids(source_folders)
for folder in source_folders:
folder_path = os.path.join('documents', folder)
if not os.path.exists(folder_path):
continue
for file_name in os.listdir(folder_path):
if file_name in files:
continue
file_path = os.path.join(folder_path, file_name)
with open(file_path, 'rb') as file:
file_content = file.read()
folder_id = folder_id_map.get(folder)
if not folder_id:
print(f"Folder ID for '{folder}' not found.")
continue
parent_reference = ItemReference(
id=folder_id
)
drive_item = DriveItem(
name=file_name,
file=File(),
parent_reference=parent_reference,
content=file_content
)
try:
response = await self._user_client.drives.by_drive_id(
os.getenv('ONEDRIVE_DEV_DRIVE_ID')
).items.post(drive_item)
print("Response", response)
except Exception as e:
print(f"Failed to upload file '{file_name}': {e}")
SDK Version
msgraph-core~=1.1.2 msgraph-sdk~=1.5.3
Latest version known to work for scenario above?
No response
Known Workarounds
No response
Debug output
Click to expand log
```APIError Code: 400 message: None error: MainError(additional_data={}, code='invalidRequest', details=None, inner_error=InnerError(additional_data={}, client_request_id='13230f64-2c2e-4083-93c1-f7325291333c', date=DateTime(2024, 8, 22, 10, 12, 34, tzinfo=Timezone('UTC')), odata_type=None, request_id='f1815677-f808-46dd-af6d-7882803e975b'), message='Invalid request', target=None)
</details>
### Configuration
- OS: Windows
- architecture: AMD64
### Other information
The 400 APIError could be more robust than just "Invalid request"
Hello @Mirciulica15 thanks for using the SDK and for reporting this,
How large is the file you want to upload, do you want to do it in chunks:
If so, use the LargeFileUploadTask from https://github.com/microsoftgraph/msgraph-sdk-python-core/
destination_path = "path/to/your_file.txt"
file_path = "path/to/your_file.txt"
async def upload_large_file():
try:
file = open(file_path, 'rb')
uploadable_properties = DriveItemUploadableProperties(
additional_data={'@microsoft.graph.conflictBehavior': 'replace'}
)
upload_session_request_body = CreateUploadSessionPostRequestBody(item=uploadable_properties)
print(f"Uploadable Properties: {uploadable_properties.additional_data}")
# can be used for normal drive uploads
try:
upload_session = await user_client.drives.by_drive_id(
"b!WtUPKhiPm0a6Bj6Z_J97a53XIv_KoNNAkdLRUrSF06lh-qW6JpABSoW62oPNb03R"
).items.by_drive_item_id('root:/my_docs/test_upload.txt:'
).create_upload_session.post(upload_session_request_body)
except APIError as ex:
print(f"Error creating upload session: {ex}")
# to be used for large file uploads
large_file_upload_session = LargeFileUploadSession(
upload_url=upload_session.upload_url,
expiration_date_time=datetime.now() + timedelta(days=1),
additional_data=upload_session.additional_data,
is_cancelled=False,
next_expected_ranges=upload_session.next_expected_ranges
)
# max_slice_size = 320 * 1024 - remove to use default
task = LargeFileUploadTask(large_file_upload_session, user_client.request_adapter, file)
total_length = os.path.getsize(file_path)
# Upload the file
# The callback
def progress_callback(uploaded_byte_range: tuple[int, int]):
print(f"Uploaded {uploaded_byte_range[0]} bytes of {total_length} bytes\n\n")
try:
upload_result = await task.upload(progress_callback)
print(f"Upload complete {upload_result}")
except APIError as ex:
print(f"Error uploading: {ex.message} - {ex.response_status_code}")
except APIError as e:
print(f"Error: {e}")
asyncio.run(upload_large_file())
Optionally, on your snippet, you can use https://learn.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0
Thank you for the reply, Shem! I managed to do it using the large file upload method 😀
Do you know whether it is possible to place the files in a specific folder within the drive? I tried to do that with an ItemReference, but it did not work out.
I would like to make a PR with my implementation in the examples directory later if that's ok, in case other people have the same use case and need help.
For anyone stumbling across this issue. You can specifiy the folder in the following line:
upload_session = await user_client.drives.by_drive_id(
"b!WtUPKhiPm0a6Bj6Z_J97a53XIv_KoNNAkdLRUrSF06lh-qW6JpABSoW62oPNb03R"
).items.by_drive_item_id('root:/path/to/folder/test_upload.txt:'
).create_upload_session.post(upload_session_request_body)
It will create the folder, if necessary.
Did anything change in the meantime? I tried the very example given by @shemogumbe and it fails with:
Error uploading: The server returned an unexpected status code and no error class is registered for this code 400 - 400
I try to upload a PDF to sharepoint. Obviously, I replaced the drive ID and upload to a different path: root:/TEST/test_upload.pdf: On top of that, I use the GraphServiceClient for user_client.
I would be very grateful if someone could point me to the right direction.
Did anything change in the meantime? I tried the very example given by @shemogumbe and it fails with:
Error uploading: The server returned an unexpected status code and no error class is registered for this code 400 - 400I try to upload a
root:/TEST/test_upload.pdf:On top of that, I use theGraphServiceClientfor user_client.I would be very grateful if someone could point me to the right direction.
I know this might seem like a weird question, but have you checked if the file was successfully uploaded? I remember that at some point I was also receiving 400, but I could see the file successfully uploaded to SharePoint.
@Mirciulica15 you are, indeed, correct: They were uploaded... Thank you!