Skip to content

storage: upload_chunks_concurrently ignores its timeout argument #18764

Open

Description

@abhishekacharya200

Determine this is the right repository

  • I determined this is the correct repository in which to report this bug.

Summary of the issue

transfer_manager.upload_chunks_concurrently takes a timeout argument and documents it as the time to wait for the server response, but it never passes it on. The XML multipart requests it makes (initiate, each part upload, finalize, and cancel when something fails) always run with the default (61, 60), so a caller cannot make a stuck upload fail sooner or give a slow link more time.

Expected: every request made by upload_chunks_concurrently uses the timeout the caller passed.

Actual: every request uses (61, 60) no matter what the caller passes.

API client name and version

google-cloud-storage 3.17.1 (main at 57366ee)

Reproduction steps: code

file: repro.py

"""Show which timeout each XML MPU request gets from upload_chunks_concurrently.

Runs the real transfer_manager and XML MPU code with a transport that records
the timeout of every request and answers like Cloud Storage would. No network.
"""

import os
import tempfile

import requests
from google.auth.credentials import AnonymousCredentials
from google.cloud.storage import Client, transfer_manager

INITIATE_BODY = (
    '<?xml version="1.0" encoding="UTF-8"?>'
    '<InitiateMultipartUploadResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">'
    "<UploadId>upload-1</UploadId></InitiateMultipartUploadResult>"
)


class RecordingTransport(requests.Session):
    is_mtls = False

    def __init__(self):
        super().__init__()
        self.calls = []

    def request(self, method, url, data=None, headers=None, timeout=None, **kwargs):
        query = url.split("?", 1)[1] if "?" in url else ""
        if method == "POST" and query == "uploads":
            step, body, extra = "initiate", INITIATE_BODY, {}
        elif method == "PUT":
            step, body, extra = "upload part", "", {"ETag": '"etag-1"'}
        elif method == "POST":
            step, body, extra = "finalize", "", {}
        else:
            step, body, extra = "cancel", "", {}
        self.calls.append((step, timeout))
        response = requests.Response()
        response.status_code = 204 if step == "cancel" else 200
        response._content = body.encode()
        response.headers.update(extra)
        response.url = url
        return response


transport = RecordingTransport()
client = Client(project="demo", credentials=AnonymousCredentials(), _http=transport)
blob = client.bucket("demo-bucket").blob("big-file.bin")

with tempfile.NamedTemporaryFile(delete=False) as handle:
    handle.write(b"x" * 10)
try:
    transfer_manager.upload_chunks_concurrently(
        handle.name,
        blob,
        chunk_size=5,
        worker_type=transfer_manager.THREAD,
        checksum=None,
        timeout=5,
    )
except Exception as error:
    print(f"raised {type(error).__name__}: {error}")
finally:
    os.unlink(handle.name)

for step, timeout in transport.calls:
    print(f"{step:12} timeout={timeout}")

Reproduction steps: supporting files

None. The script answers the requests itself instead of using the network.

Reproduction steps: actual results

initiate     timeout=(61, 60)
upload part  timeout=(61, 60)
upload part  timeout=(61, 60)
finalize     timeout=(61, 60)

Reproduction steps: expected results

initiate     timeout=5
upload part  timeout=5
upload part  timeout=5
finalize     timeout=5

OS & version + platform

Linux 7.0.0

Python environment

Python 3.12.3

Python dependencies

google-api-core 2.42.0, google-auth 2.62.0, google-cloud-core 2.8.0, google-crc32c 1.9.0, requests 2.34.2

Additional context

XMLMPUContainer.initiate, finalize and cancel and XMLMPUPart.upload already accept timeout, so the fix is to pass it through, including into the _upload_part worker. I'm happy to send a PR for it.

Activity

  1. added a commit that references this issue on Oct 19, 2026
    ccc12b7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions