python-slack-sdk icon indicating copy to clipboard operation
python-slack-sdk copied to clipboard

Sending a message with file after uploading the file gives file not found error

Open nursimaaigoritma opened this issue 1 year ago • 4 comments

Hello, I upload a file to Slack, and then send a message to a channel referencing that file. However, if I call them immediately after each other, sending message fails saying it could not find the file. If I wait for about 1 sec between these two operations then it works, I can see the image in the sent message in Slack.

Reproducible in:

slack_sdk==3.27.1
Python 3.10.13
ProductName:            macOS
ProductVersion:         14.1.1
BuildVersion:           23B81
Darwin Kernel Version 23.1.0: Mon Oct  9 21:27:24 PDT 2023; root:xnu-10002.41.9~6/RELEASE_ARM64_T6000

The Slack SDK version

slack_sdk==3.27.1

Python runtime version

Python 3.10.13

OS info

ProductName: macOS ProductVersion: 14.1.1 BuildVersion: 23B81 Darwin Kernel Version 23.1.0: Mon Oct 9 21:27:24 PDT 2023; root:xnu-10002.41.9~6/RELEASE_ARM64_T6000

Steps to reproduce:

from slack_sdk import WebClient
import ssl
import certifi
import datetime

token = ""
channel_id = ""
img_path = ""

ssl_context = ssl.create_default_context(cafile=certifi.where())
client = WebClient(token, ssl=ssl_context)

fd = open(img_path, 'rb')
slack_response = client.files_upload_v2(
    file=fd.read(),
    alt_txt="screenshot"
)
fd.close()

print(slack_response)

message = [
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "*Bug Report* :ladybug:"
                }
            },
            {
                "type": "section",
                "fields": [
                    {
                        "type": "mrkdwn",
                        "text": "*Company:*\nCompany"
                    },
                    {
                        "type": "mrkdwn",
                        "text": "*User Name:*\nUser name"
                    },
                    {
                        "type": "mrkdwn",
                        "text": "*Browser Type:*\nBrowser Type"
                    },
                    {
                        "type": "mrkdwn",
                        "text": f"*Timestamp:*\n{datetime.datetime.now().strftime('%d/%m/%Y %H:%M:%S')}"
                    },
                    {
                        "type": "mrkdwn",
                        "text": "*Title:*\nTitle"
                    },
                    {
                        "type": "mrkdwn",
                        "text": "*Description:*\nDescription"
                    },
                    {
                        "type": "mrkdwn",
                        "text": "*Error Message:*\nError Message"
                    }
                ]
            },
           {
                "type": "image",
                "alt_text": "screenshot",
                "slack_file": {
                    "id": slack_response["file"]["id"]
                }
            }
        ]


client.chat_postMessage(
    channel=channel_id,
    blocks= message,
)

The output is

{'ok': True, 'files': [{'id': 'F07AZJY97DE', 'created': 1719608394, ...}

Traceback (most recent call last):
  File "...", line 73, in <module>
    client.chat_postMessage(
  File "myenv/lib/python3.10/site-packages/slack_sdk/web/client.py", line 2564, in chat_postMessage
    return self.api_call("chat.postMessage", json=kwargs)
  File "myenv/lib/python3.10/site-packages/slack_sdk/web/base_client.py", line 155, in api_call
    return self._sync_send(api_url=api_url, req_args=req_args)
  File "myenv/lib/python3.10/site-packages/slack_sdk/web/base_client.py", line 186, in _sync_send
    return self._urllib_api_call(
  File "myenv/lib/python3.10/site-packages/slack_sdk/web/base_client.py", line 317, in _urllib_api_call
    ).validate()
  File "myenv/lib/python3.10/site-packages/slack_sdk/web/slack_response.py", line 199, in validate
    raise e.SlackApiError(message=msg, response=self)
slack_sdk.errors.SlackApiError: The request to the Slack API failed. (url: https://www.slack.com/api/chat.postMessage)
The server responded with: {'ok': False, 'error': 'invalid_blocks', 'errors': ['invalid slack file [json-pointer:/blocks/2/slack_file.id/slack_file]'], 'response_metadata': {'messages': ['[ERROR] invalid slack file [json-pointer:/blocks/2/slack_file.id/slack_file]']}}

If I add time.sleep(1) just before sending message it works fine. It sends the message with attached file. Sometimes up to 0.5 sec also works.

Expected result:

I expect file upload to be observable with less latency.

Actual result:

SlackApiError("The request to the Slack API failed. (url: https://www.slack.com/api/chat.postMessage)\nThe server responded with: {'ok': False, 'error': 'invalid_blocks', 'errors': ['invalid slack file [json-pointer:/blocks/2/slack_file.id/slack_file]'], 'response_metadata': {'messages': ['[ERROR] invalid slack file [json-pointer:/blocks/2/slack_file.id/slack_file]']}}")

nursimaaigoritma avatar Jun 28 '24 21:06 nursimaaigoritma

Thanks for submitting this issue, @nursimaaigoritma! 🙌

Looking over it initially, it does appear to be a latency issue as you identified, but is a bit confusing since it does seem that the file upload API call has fully executed, which indicates a file was uploaded successfully.

Before examining that side of this, I did want to see if using the url param instead of id within your slack_file Block made a difference. You could do something like this:

file_url = slack_response.get("file").get("permalink")

...
# within your message block kit, replace the image block using `slack_files` with this:
 {
        "type": "image",
        "alt_text": "screenshot",
        "slack_file": {
                "id": file_url
            }
}
...

Just wanted to see if using the permalink of the file made a difference or not with file retrieval in this case vs. the ID! You can learn more in this guide.

hello-ashleyintech avatar Jun 28 '24 21:06 hello-ashleyintech

It gives another error (again while sending the message):

The server responded with: {'ok': False, 'error': 'invalid_blocks', 'errors': ['input must match regex pattern: ^[F][A-Z0-9]{8,}$ [json-pointer:/blocks/2/slack_file/id]'], 'response_metadata': {'messages': ['[ERROR] input must match regex pattern: ^[F][A-Z0-9]{8,}$ [json-pointer:/blocks/2/slack_file/id]']}}

Url was like this

https://our-workspace.slack.com/files/xxxxxxxxxxx/xxxxxxxxxxx/uploaded_file

nursimaaigoritma avatar Jun 28 '24 21:06 nursimaaigoritma

@nursimaaigoritma files.upload v2 has a slight delay in completing the file upload process. Therefore, for this use case, your app needs to poll the files.info API endpoint with the uploaded file IDs until the shares properties are populated (indicating that the files are successfully uploaded and shared). Please refer to https://github.com/slackapi/python-slack-sdk/issues/1329#issuecomment-1430589611 for more details.

If you try to embed the file permalink in a block before the process is completed, displaying the block can result in the error you've encountered. I understand this could be frustrating but I hope this clarifies.

seratch avatar Jun 29 '24 00:06 seratch

Yes this absolutely clarifies. Thank you for the response. Would you consider adding it to documentation (or I may have missed it)? As I understand, making files_upload_v2 asynchronous was a deliberate choice and polling is the only way.

nursimaaigoritma avatar Jun 29 '24 18:06 nursimaaigoritma

Can I assume file upload is completed if client.files_info(file=file_id) returns "ok": True?

I try this

# uploading file is same
# ...

# check if completed
import polling
polling.poll(
    lambda: client.files_info(file=file_id)["ok"],
    step=0.5,
    timeout=10
)

# At this point, I assume file upload is complete and I can use the id in a message
client.chat_postMessage(
    channel=channel_id,
    blocks= message,
)

But the problem persists. Should I check something else in the "files_info" response?

nursimaaigoritma avatar Jul 01 '24 12:07 nursimaaigoritma

Should I check something else in the "files_info" response?

Yes, you need to check the updates on the file metadata. Specifically, mime_type, shares, and a few others will be filled once the upload process completes.

seratch avatar Jul 03 '24 06:07 seratch