Uploaded files are not appearing in channel and do not contain mimetype
I am trying to upload a file to a private Slack channel using the Slack API via the following Python script. However, once uploaded, the file cannot be downloaded by users in the channel. I am looking for help to understand why this is happening and how to resolve it.
Python runtime version
3.12.1
I used these step:
- https://slack.com/api/files.getUploadURLExternal
- https://slack.com/api/files.completeUploadExternal
- https://slack.com/api/files.sharedPublicURL
Actual result:
{
"ok": true,
"file": {
"id": "XXXXXXXXXX",
"created": 1729848820,
"timestamp": 1729848820,
"name": "xxxxxxxxxxxxxxxxxx.xxx",
"title": "xxxxxxxxxxxxxxxxxxxx.xxx",
"mimetype": "",
"filetype": "",
"pretty_type": "",
"user": "XXXXXXXXX",
"user_team": "XXXXXXXX",
"editable": false,
"size": 61652577,
"mode": "hosted",
"is_external": false,
"external_type": "",
"is_public": false,
"public_url_shared": true,
"display_as_bot": false,
"username": "",
"url_private": "https://files.slack.com/files-pri/xxxxxxxxx-xxxxxxxxxx/xxxxxxxxxxxxxxxxxx.xxx",
"url_private_download": "https://files.slack.com/files-pri/xxxxxxxxxxxxx-xxxxxxx/download/xxxxxxxxxxxxxxxxxx.xxx",
"media_display_type": "unknown",
"permalink": "https://xxxxxxxxx.slack.com/files/xxxxxxxxxx/xxxxxxxxxxxxxxx/xxxxxxxxxxxxx.xxx",
"permalink_public": "https://slack-files.com/xxxxxxxxxx-xxxxxxxxxxxxx-xxxxxxxxxxxxx",
"comments_count": 0,
"is_starred": false,
"shares": {},
"channels": [],
"groups": [],
"ims": [],
"has_more_shares": false,
"has_rich_preview": false,
"file_access": "visible"
}
}
Hi @nobikin95 - I noticed that the successful output has is_public: false, and the channels array is empty. What was your output for the files.completeUploadExternal request? I just want to check here that you supplied the channel_id, otherwise the file will be private.
Hi @srajiang, output for the files.completeUploadExternal :
"ok": true,
"files": [
{
"id": "xxxxxxx",
"created": 1729847794,
"timestamp": 1729847794,
"name": "xxxxxxxxxxxxx.xxx",
"title": "xxxxxxxxxxx.xxx",
"mimetype": "",
"filetype": "",
"pretty_type": "",
"user": "xxxxxxxxx",
"user_team": "xxxxxxxxx",
"editable": false,
"size": 61652512,
"mode": "hosted",
"is_external": false,
"external_type": "",
"is_public": false,
"public_url_shared": false,
"display_as_bot": false,
"username": "",
"url_private": "https://files.slack.com/files-pri/XXXXXXXXXXXXX-XXXXXXXXXXX/xxxxxxxxxxxxxxxxx.xxx",
"url_private_download": "https://files.slack.com/files-pri/XXXXXXXXXXXXX-XXXXXXXXXXX/download/xxxxxxxxxxxxxxxxx.xxx",
"media_display_type": "unknown",
"permalink": "https://ikameglobal.slack.com/files/XXXXXXXXXXXXX/XXXXXXXXXXXXX/xxxxxxxxxxxxxxxxx.xxx",
"permalink_public": "https://slack-files.com/XXXXXXXXXXXXX-XXXXXXXXXXXXX-xxxxxxxxxx",
"comments_count": 0,
"is_starred": false,
"shares": {},
"channels": [],
"groups": [],
"ims": [],
"has_more_shares": false,
"has_rich_preview": false,
"file_access": "visible"
}
]
}
It seems like I'm having an issue with channel_id, here is the code I'm using:
#!/bin/bash
FILE_PATH=$1
CHANNEL_ID="XXXXXXXXXXX"
USER_TOKEN="xoxp-"
BOT_TOKEN="xoxb-"
# Check if the file exists
echo "Checking if file exists at: $FILE_PATH"
if [ ! -f "$FILE_PATH" ]; then
echo "Error: File $FILE_PATH does not exist!"
exit 1
fi
echo "File found at: $FILE_PATH"
# Get file size (use command compatible with macOS and Linux)
if [[ "$OSTYPE" == "darwin"* ]]; then
FILE_SIZE=$(stat -f%z "$FILE_PATH")
else
FILE_SIZE=$(stat -c%s "$FILE_PATH")
fi
# Step 1: Get upload URL using the files.getUploadURLExternal API
echo "Fetching upload URL from Slack API..."
UPLOAD_URL_RESPONSE=$(curl -s -X POST \
-H "Authorization: Bearer $USER_TOKEN" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data "filename=$(basename "$FILE_PATH")&length=$FILE_SIZE&channels=$CHANNEL_ID" \
https://slack.com/api/files.getUploadURLExternal)
# Log the response from the API
echo "Response from files.getUploadURLExternal: $UPLOAD_URL_RESPONSE"
# Extract upload URL and file_id from JSON response
UPLOAD_URL=$(echo "$UPLOAD_URL_RESPONSE" | grep -o '"upload_url":"[^"]*' | sed 's/"upload_url":"//g' | sed 's/\\//g')
FILE_ID=$(echo "$UPLOAD_URL_RESPONSE" | grep -o '"file_id":"[^"]*' | sed 's/"file_id":"//g')
# Check if the URL or file_id was not obtained
if [ -z "$UPLOAD_URL" ] || [ -z "$FILE_ID" ]; then
echo "Error: Failed to get upload URL or file_id"
exit 1
fi
echo "Upload URL: $UPLOAD_URL"
echo "File ID: $FILE_ID"
# Step 2: Upload the file to the obtained URL
echo "Uploading file to: $UPLOAD_URL"
UPLOAD_RESULT=$(curl -s -X PUT \
-H "Content-Type: application/octet-stream" \
--data-binary @"$FILE_PATH" \
"$UPLOAD_URL")
# Log the upload result
echo "Result of file upload: $UPLOAD_RESULT"
# Step 3: Complete the upload using the files.completeUploadExternal API
echo "Completing file upload with Slack API..."
COMPLETE_RESPONSE=$(curl -s -X POST \
-H "Authorization: Bearer $USER_TOKEN" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "files=[{\"id\":\"$FILE_ID\"}]" \
--data-urlencode "channels=$CHANNEL_ID" \
https://slack.com/api/files.completeUploadExternal)
# Log the response from the files.completeUploadExternal API
echo "Response from files.completeUploadExternal: $COMPLETE_RESPONSE"
# Step 3.1: Share the public URL using the files.sharedPublicURL API
echo "Sharing file publicly using Slack API..."
SHARED_PUBLIC_URL_RESPONSE=$(curl -s -X POST \
-H "Authorization: Bearer $USER_TOKEN" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data "file=$FILE_ID" \
https://slack.com/api/files.sharedPublicURL)
# Log the response from the files.sharedPublicURL API
echo "Response from files.sharedPublicURL: $SHARED_PUBLIC_URL_RESPONSE"
# Extract URL from JSON response (use permalink instead of permalink_public)
PUBLIC_URL=$(echo "$SHARED_PUBLIC_URL_RESPONSE" | grep -o '"permalink_public":"[^"]*' | sed 's/"permalink_public":"//g' | sed 's/\\//g')
# Check if the URL was not obtained
if [ -z "$PUBLIC_URL" ]; then
echo "Error: Failed to get permalink URL"
exit 1
fi
echo "Public URL: $PUBLIC_URL"
# Step 4: Post a message to the channel with the uploaded file using chat.postMessage API
echo "Posting message to channel with uploaded file ..."
POST_MESSAGE_RESPONSE=$(curl -s -X POST \
-H "Authorization: Bearer $USER_TOKEN" \
-H "Content-Type: application/json" \
--data "$(cat <<EOF
{
"channel": "$CHANNEL_ID",
"text": "Build Success: <$PUBLIC_URL>",
"attachments": [
{
"title": "Click here to download",
"title_link": "$PUBLIC_URL",
"text": "The file is available for public download."
}
]
}
EOF
)" \
https://slack.com/api/chat.postMessage)
# Log the response from the chat.postMessage API
echo "Response from chat.postMessage: $POST_MESSAGE_RESPONSE"
The cause of your confusion may not be related to this, but the files.completeUploadExternal API processes its tasks asynchronously. This means that if a subsequent operation attempts to download the uploaded file immediately, it might fail until the files.completeUploadExternal operation completes. It appears that your code is trying to download the files right away. In most cases, this can fail regardless of whether the files are public or private.
One way to determine if the upload operation is finished is to check if the "shares" property is available in the file's metadata. You can verify this by polling the files.info API with the uploaded file's ID.
π It looks like this issue has been open for 30 days with no activity. We'll mark this as stale for now, and wait 10 days for an update or for further comment before closing this issue out. If you think this issue needs to be prioritized, please comment to get the thread going again! Maintainers also review issues marked as stale on a regular basis and comment or adjust status if the issue needs to be reprioritized.
I've been stumped by this api. My file gets uploaded and I've set the channel_id but it still is not seeing it in the channel. From the documentation it seems like the file should show up in the channel but perhaps that is not the case?
calling files.completeUploadExternal (I'm using Elixir) with this body:
%{
files: [%{id: "xxxxxxxxx", title: "Galaxy Book Pro.pdf"}],
thread_ts: "xxxxxxxx.xxxxxx",
initial_comment: "Attachments",
channel_id: "xxxxxxxxxx"
}
I get this response:
%{
"files" => [
%{
"filetype" => "",
"mode" => "hosted",
"comments_count" => 0,
"public_url_shared" => false,
"file_access" => "visible",
"is_starred" => false,
"channels" => [],
"url_private" => "https://files.slack.com/files-pri/xxxxxxxx-xxxxxxxx/galaxy_book_pro.pdf",
"url_private_download" => "https://files.slack.com/files-pri/xxxxxxxx-xxxxxxxx/download/galaxy_book_pro.pdf",
"created" => 1733998180,
"media_display_type" => "unknown",
"external_type" => "",
"mimetype" => "",
"has_rich_preview" => false,
"pretty_type" => "",
"ims" => [],
"groups" => [],
"shares" => %{},
"name" => "Galaxy Book Pro.pdf",
"id" => "xxxxxxxxxxx",
"display_as_bot" => false,
"user" => "xxxxxxxxxx",
"size" => 45061,
"has_more_shares" => false,
"permalink_public" => "https://slack-files.com/xxxxxxxx-xxxxxxx-xxxxxxx",
"timestamp" => 1733998180,
"user_team" => "xxxxxxxxx",
"permalink" => "https://upptec.slack.com/files/xxxxxxxx/xxxxxxxx/galaxy_book_pro.pdf",
"title" => "Galaxy Book Pro.pdf",
"is_external" => false,
"username" => "",
"editable" => false,
"is_public" => false
}
],
"ok" => true
}
Using files.info I can find the file:
{
"ok": true,
"file": {
"id": "xxxxxxxxxx",
"created": 1733998180,
"timestamp": 1733998180,
"name": "Galaxy Book Pro.pdf",
"title": "Galaxy Book Pro.pdf",
"mimetype": "",
"filetype": "",
"pretty_type": "",
"user": "xxxxxxxxxx",
"user_team": "xxxxxxxx",
"editable": false,
"size": 45061,
"mode": "hosted",
"is_external": false,
"external_type": "",
"is_public": false,
"public_url_shared": false,
"display_as_bot": false,
"username": "",
"url_private": "https://files.slack.com/files-pri/xxxxxxx-xxxxxxx/galaxy_book_pro.pdf",
"url_private_download": "https://files.slack.com/files-pri/xxxxxxxx-xxxxxxxx/download/galaxy_book_pro.pdf",
"media_display_type": "unknown",
"permalink": "https://X.slack.com/files/xxxxxxxxxx/xxxxxxxx/galaxy_book_pro.pdf",
"permalink_public": "https://slack-files.com/xxxxxxxxx-xxxxxxxx-xxxxxx",
"favorites": [],
"is_starred": false,
"shares": {},
"channels": [],
"groups": [],
"ims": [],
"has_more_shares": false,
"has_rich_preview": false,
"file_access": "visible",
"comments_count": 0
},
"comments": [],
"response_metadata": {
"next_cursor": ""
}
}
Note: I've redacted ids
π It looks like this issue has been open for 30 days with no activity. We'll mark this as stale for now, and wait 10 days for an update or for further comment before closing this issue out. If you think this issue needs to be prioritized, please comment to get the thread going again! Maintainers also review issues marked as stale on a regular basis and comment or adjust status if the issue needs to be reprioritized.
As this issue has been inactive for more than one month, we will be closing it. Thank you to all the participants! If you would like to raise a related issue, please create a new issue which includes your specific details and references this issue number.
bumping this because i'm having the same issue. when calling files.completeUploadExternal and passing either channel_id or channels, the file does not get shared with the channel. there are no errors. and checking files.info confirms the file was properly uploaded just not shared. I believe this is a bug in the slack api and not the python sdk since I have reproduced this behavior with the slack api tester, curl, and the ruby sdk. it's sdk agnostic.
π Thank y'all for sharing all of these outputs and examples. I'm going to reopen this issue since this might be a backend issue that we should keep track of.
I'm noticing that the mimetype attribute is empty in all of the snippets shared and this is often a good signal of a "complete" file upload. We recommend polling for this to appear, but if it never does then a problem with the file contents being processed seems to be happening π
{
"ok": true,
"file": {
"id": "xxxxxxxxxx",
"mimetype": ""
}
}
For now I don't have a recommendation or workaround, but I'm curious if this is always happening for the same files or if it's otherwise sporadic?
I also noticed the missing mimetype. But the file appears to have uploaded successfully (regardless of which file I use). It just wonβt share to the channel.
On Thu, May 22, 2025 at 8:01β―PM Eden Zimbelman @.***> wrote:
zimeg left a comment (slackapi/python-slack-sdk#1575) https://github.com/slackapi/python-slack-sdk/issues/1575#issuecomment-2902977891
π Thank y'all for sharing all of these outputs and examples. I'm going to reopen this issue since this might be a backend issue that we should keep track of.
I'm noticing that the mimetype attribute is empty in all of the snippets shared and this is often a good signal of a "complete" file upload. We recommend polling for this to appear, but if it never does then a problem with the file contents being processed seems to be happening π
{ "ok": true, "file": { "id": "xxxxxxxxxx", "mimetype": "" } }
For now I don't have a recommendation or workaround, but I'm curious if this is always happening for the same files or if it's otherwise sporadic?
β Reply to this email directly, view it on GitHub https://github.com/slackapi/python-slack-sdk/issues/1575#issuecomment-2902977891, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJ5QKPWKLSY5U4OLLVF2XL27ZXORAVCNFSM6AAAAABQS7AYB6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDSMBSHE3TOOBZGE . You are receiving this because you commented.Message ID: @.***>
Struggling with the same issue.
I'm having the same problem.
I'm also having the same problem, but with the JavaScript SDK
EDIT: My issue was I let AI edit the code and it changed the HTTP POST request uploading the data to a PUT request which is incorrect, the API returns an OK response but it's not OK. I assumed the AI knew what it was doing when it did that and forgot all about it. Hours later and spying on the connection after being given a working workflow by Slack, I was able to ascertain the issue. The API unhelpfully returns an OK response to PUT but it's not OK!
Eggs on my face really though.
Facing this issue as well, looks like images gets shared to channel normally, but other documents like PDF don't.
same issue here, for me images too are also not uploading.
same issue here, in python SDK, The mimetype and channels are empty :( attachments does not shared :(
hi, i have found this solution, seems this is not a bug. to use the file upload utility, we should have 3 steps: step1: post file to "getUploadURLExternal" to get a upload url step2: post file again to upload url which respond in step1 step3: use file through "completeUploadExternal"
π Thank y'all for sharing all of these outputs and examples. I'm going to reopen this issue since this might be a backend issue that we should keep track of.
I'm noticing that the
mimetypeattribute is empty in all of the snippets shared and this is often a good signal of a "complete" file upload. We recommend polling for this to appear, but if it never does then a problem with the file contents being processed seems to be happening π{ "ok": true, "file": { "id": "xxxxxxxxxx", "mimetype": "" } } For now I don't have a recommendation or workaround, but I'm curious if this is always happening for the same files or if it's otherwise sporadic?
Hi, was your investigation able to reveal anything regarding this issue? Before, we were able to still use the old files.upload API, but as that endpoint is now deprecated, we are running into this issue when trying to switch over to the new file upload workflow.
@sidkurella The files.upload method had been deprecated since April 2024 and is sunset today, as you're finding:
π° https://docs.slack.dev/changelog/2024-04-a-better-way-to-upload-files-is-here-to-stay/
I understand that the mimetype might not appear in immediate response from the filesUploadV2 method since this initial response happens when the call to the files.completeUploadExternal method completes.
That initial response can be misleading because the file is still being processed on the server, even after the upload is complete. The mimetype won't appear with value until the processing finishes from what I can tell.
Polling the files.info method for this might be a suitable workaround, but this requires an additional files:read scope.
@zimeg Well, it's been several hours and I still don't see the file in my channel. I can investigate further tomorrow.
We're attempting to migrate one of our bots which relies on file upload to the new APIs and despite trying several things, the uploaded file attached to the messages cannot be downloaded or accessed by anyone in the channel.
Please let us know how we can help resolve this issue as it's a critical part of our operations.
We're passing the correct channel_id, our bot has the right permissions/scopes and we're using all the recommended guidance related to the new v2 API. We had no issues with the old API.
Note: I'm not using this Python SDK, I've built my own handling code in Go. But I'm running into the same exact issue as everyone else in this thread.
Hi, I found a way that works for me. Steps:
-
files_getUploadURLExternal
-
with open(FilePath, "rb") as f: file_bytes = f.read()
headers2 = { "Content-Type": "application/octet-stream" } resp = requests.post(upload_url, headers=headers2, data=file_bytes) resp.raise_for_status()
-
files_completeUploadExternal
@Vivekxk Yeah I'm experiencing the same thing, using this client: https://github.com/slack-go/slack
@zimeg Sorry for the delay, we can see that mimetype is empty and the list of channels is also empty after calling files.info but the difference is that we never see that the file appears in the channel (not even after multiple days). The file has a permalink but when I try to access it I just get a blank screen.
@sidkurella Likewise, I apologize for slow follow up here π β¨
I'm curious if https://github.com/slack-go/slack/issues/1481 might hint at the cause of missing mimetype being an unexpected length argument for the file size?
A snippet of "broken" code might also be useful in troubleshooting! π
@zimeg Sure, here's a snippet of the code:
func (n *Messenger) SendFileMessage(ctx context.Context, channel string, inputFile *os.File, initialComment string) {
fileName := filepath.Base(inputFile.Name())
fileInfo, err := inputFile.Stat()
if err != nil {
n.log.Errorf("Could not send slack message to channel %s, skipping, err=%v", fileName, err)
return
}
fileSize := int(fileInfo.Size())
request := slack.UploadFileV2Parameters{
Channel: channel,
InitialComment: initialComment,
Reader: inputFile,
Filename: fileName,
FileSize: fileSize,
AltTxt: initialComment,
Title: initialComment,
SnippetType: "csv",
}
fileSummary, err := n.slackClient.UploadFileV2Context(ctx, request)
if err != nil {
n.log.Errorf("Could not send slack message to channel %s, skipping, err=%+v, msg=%s, request: %+v", channel, err, initialComment, request)
}
n.log.Infof("Successfully uploaded file to slack channel %s, file summary: %+v", channel, fileSummary)
// clipped ...
We're not seeing any returned errors. Below this we call GetFileInfo:
file, comments, paging, err := n.slackClient.GetFileInfo(fileSummary.ID, 1, 1)
if err != nil {
n.log.Fatalf("file info failed: %v", err)
}
n.log.Debugf("File info: %+v, comments: %+v, paging: %+v", file, comments, paging)
and here we observe the following file info:
File info: &{ID:redacted Created:"Wed Nov 19" Timestamp:"Wed Nov 19" Name:redacted.csv Title:redacted Mimetype: ImageExifRotation:0 Filetype: PrettyType: User:redacted Mode:hosted Editable:false IsExternal:false ExternalType: Size:1718 URL: URLDownload: URLPrivate:https://files.slack.com/redacted URLPrivateDownload:https://files.slack.com/redacted OriginalH:0 OriginalW:0 Thumb64: Thumb80: Thumb160: Thumb360: Thumb360Gif: Thumb360W:0 Thumb360H:0 Thumb480: Thumb480W:0 Thumb480H:0 Thumb720: Thumb720W:0 Thumb720H:0 Thumb960: Thumb960W:0 Thumb960H:0 Thumb1024: Thumb1024W:0 Thumb1024H:0 Permalink:redacted PermalinkPublic:redacted EditLink: Preview: PreviewHighlight: Lines:0 LinesMore:0 IsPublic:false PublicURLShared:false Channels:[] Groups:[] IMs:[] InitialComment:{ID: Created:"Thu Jan 1" Timestamp:"Thu Jan 1" User: Comment:} CommentsCount:0 NumStars:0 IsStarred:false Shares:{Public:map[] Private:map[]} Subject: To:[] From:[] Cc:[] Headers:{Date: InReplyTo: ReplyTo: MessageID:}}
The application can download the file from URLPrivateDownload but we see diffs when we download that, we're investigating further today. If I try to access the file via Permalink I get a blank screen that does show the correct title of the file though.
When we download that url we get some kind of sign-in page instead of the actual file (we're downloading in the same application that did the upload and it has files:read scope so I doubt it's a permissions error)
@sidkurella This is so appreciated! I'll plan to soon investigate this go implementation πΎ
When we download that url we get some kind of sign-in page instead of the actual file
The same token used when uploading a file must be included as an authorization header when downloading from the private download URL - https://github.com/slackapi/python-slack-sdk/issues/1434 - otherwise that authentication page is returned I do believe!
@zimeg Oops, yeah I was setting the Authorization header wrong. After setting it properly the downloaded file from URLPrivateDownload and the uploaded file are identical, but still not seeing it shared to any channels.