Retrying internal errors in files_upload_v2
Hi there,
We occasionally see transient errors from the Slack API in calls to files.completeUploadExternal that are done within the SDK's implementation of files_upload_v2. The error we see most frequently error we encounter is file_update_failed, which has more or less no useful explanation on the API docs. Support has also not been very helpful in understanding the nature of these issues: they just said there is a temporary server error, and we should retry the call to completeUploadExternal
Can the Python SDK implement that retr? It doesn't seem like the caller of files_upload_v2 (i.e. our code) could, since that would redo the entire upload. The caller also doesn't have enough information to retry the files.completeUploadExternal call, since the ID is lost when the SDK throws an exception.
I suppose a workaround is to install a custom RetryHandler on the WebClient. But like in #1715, shouldn't that just be part of the SDK?
Category
- [x] slack_sdk.web.WebClient (sync/async) (Web API client)
Hi @tjstum thanks for bringing this up 💯
Adding some sort of retry handler for file_update_failed on a request to files.completeUploadExternal seems like a logical feature, especially since this all happens within the files_upload_v2 method. In order to implement this correctly, we should add a way to pass specific retry handlers per api_method to reduce bloating.
Until then, here is an implementation of a FileUploadFailRetryHandler that can be used as a workaround until we figure out the proper way to implement all this.
import os
import logging
from pathlib import Path
from typing import Optional
from slack_sdk import WebClient
from slack_sdk.http_retry import RetryHandler, RetryState, HttpRequest, HttpResponse, default_retry_handlers
from slack_sdk.http_retry.handler import default_interval_calculator
logging.basicConfig(level=logging.INFO)
FILE_PATH = Path("./README.md")
FILE_NAME = "README.md"
class FileUploadFailRetryHandler(RetryHandler):
"""Custom Retry handler for file_update_failed errors during file uploads."""
def __init__(
self,
max_retry_count: int = 1,
interval_calculator=default_interval_calculator,
api_methods=["files.completeUploadExternal"],
):
super().__init__(max_retry_count, interval_calculator)
self.api_methods = set(api_methods)
def _can_retry(
self,
*,
state: RetryState,
request: HttpRequest,
response: Optional[HttpResponse] = None,
error: Optional[Exception] = None,
) -> bool:
if error is not None or response is None:
return False
request_url = request.url
if not any(api_method in request_url for api_method in self.api_methods):
return False
body = response.body
if body.get("ok", False) is False:
if body.get("error") == "file_update_failed":
return True
return False
def main():
retry_handlers = default_retry_handlers()
retry_handlers.append(FileUploadFailRetryHandler())
client = WebClient(token=os.environ.get("SLACK_BOT_TOKEN"), retry_handlers=retry_handlers)
with open(FILE_PATH, "rb") as file:
file_content = file.read()
result = client.files_upload_v2(
channel="C123", # You can change this to your target channel
content=file_content,
filename=FILE_NAME,
title="Bolt for Python file upload",
initial_comment="Here's the file for our project",
)
client.logger.info(f"File uploaded: {result['file']['id']}")
if __name__ == "__main__":
main()
Thanks. We'll give this a shot for now. We see this error infrequently (5 times in August), but it always confuses our users.
I'll stay tuned for how you want to implement this within files_upload_v2!
👋 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.