{"record":{"id":"af4fd179e9f618ed","repo":"duplicati/duplicati","slug":"failed-to-start-upload-session","errorCode":null,"errorMessage":"Failed to start upload session","messagePattern":"Failed to start upload session","errorType":"http","errorClass":"HttpRequestException","httpStatus":null,"severity":"error","filePath":"Duplicati/Library/Backend/GoogleServices/GoogleCommon.cs","lineNumber":117,"sourceCode":"        /// <param name=\"readWriteTimeout\">The read write timeout.</param>\r\n        /// <param name=\"method\">The HTTP Method.</param>\r\n        /// <typeparam name=\"TRequest\">The type of data to upload as metadata.</typeparam>\r\n        /// <typeparam name=\"TResponse\">The type of data returned from the upload.</typeparam>\r\n        public static async Task<TResponse> ChunkedUploadWithResumeAsync<TRequest, TResponse>(JsonWebHelperHttpClient oauth, TRequest requestdata, string url, Stream stream, TimeSpan shortTimeout, TimeSpan readWriteTimeout, CancellationToken cancelToken, HttpMethod method)\r\n            where TRequest : class\r\n            where TResponse : class\r\n        {\r\n            using var req = await oauth.CreateRequestAsync(url, method, cancelToken);\r\n            if (requestdata != null)\r\n                req.Content = JsonContent.Create(requestdata);\r\n            req.Headers.Add(\"X-Upload-Content-Type\", \"application/octet-stream\");\r\n            req.Headers.Add(\"X-Upload-Content-Length\", stream.Length.ToString());\r\n\r\n            var uploaduri = await Utility.Utility.WithTimeout(shortTimeout, cancelToken, async ct =>\r\n            {\r\n                using var resp = await oauth.GetResponseAsync(req, HttpCompletionOption.ResponseContentRead, ct).ConfigureAwait(false);\r\n                if (!resp.Headers.TryGetValues(\"Location\", out var locationValues) || string.IsNullOrWhiteSpace(locationValues.FirstOrDefault()))\r\n                    throw new HttpRequestException(HttpRequestError.Unknown, \"Failed to start upload session\", null, resp.StatusCode);\r\n\r\n                return locationValues.First();\r\n            }).ConfigureAwait(false);\r\n\r\n            if (string.IsNullOrWhiteSpace(uploaduri))\r\n                throw new Exception(\"Failed to start upload session\");\r\n\r\n            return await ChunkedUploadAsync<TResponse>(oauth, uploaduri, stream, shortTimeout, readWriteTimeout, cancelToken).ConfigureAwait(false);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Helper method that performs a chunked upload, and queries for http status after each chunk\r\n        /// </summary>\r\n        /// <returns>The response item</returns>\r\n        /// <param name=\"oauth\">The Oauth instance</param>\r\n        /// <param name=\"uploaduri\">The resumeable uploaduri</param>\r\n        /// <param name=\"stream\">The stream with data to upload.</param>\r\n        /// <param name=\"shortTimeout\">The short request timeout.</param>\r","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/duplicati/duplicati/blob/3f348be3e33f5d72d414e3ad55839c2ba34dda67/Duplicati/Library/Backend/GoogleServices/GoogleCommon.cs#L99-L135","documentation":"ChunkedUploadWithResumeAsync starts a resumable session by POSTing metadata; GCS must return a Location header with the upload URI. If the Location header is absent or empty, Duplicati throws HttpRequestException (Unknown error) 'Failed to start upload session' with the response StatusCode attached. A second, defensive Exception with the same message covers the unreachable case where the header existed but the returned value is still whitespace.","triggerScenarios":"The session-initiation POST returns 2xx but no Location header, or returns a non-2xx (the body of the if is only reached on the TryGetValues path). Common when auth/permission/quota prevents session creation but the response shape omits Location.","commonSituations":"Service account lacks permission to create objects in the bucket; bucket does not exist (so init 404s and no Location); resumable-upload feature disabled; a proxy strips the Location header; transient GCS error returning an empty body.","solutions":["Confirm the bucket exists and the account has storage.objects.create on it.","Capture the initiation response status/body — a non-2xx here usually carries the real error.","Ensure no intermediary (proxy/SDK wrapper) strips the Location response header.","Retry once for transient 5xx; if it persists, escalate with the captured status code."],"exampleFix":"// before\nif (!resp.Headers.TryGetValues(\"Location\", out var locationValues) || string.IsNullOrWhiteSpace(locationValues.FirstOrDefault()))\n    throw new HttpRequestException(HttpRequestError.Unknown, \"Failed to start upload session\", null, resp.StatusCode);\n\n// after (include body + status for diagnosis)\nif (!resp.Headers.TryGetValues(\"Location\", out var locationValues) || string.IsNullOrWhiteSpace(locationValues.FirstOrDefault()))\n{\n    var body = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false);\n    throw new HttpRequestException(HttpRequestError.Unknown,\n        $\"Failed to start upload session (status {(int)resp.StatusCode}): {body}\",\n        null, resp.StatusCode);\n}","handlingStrategy":"try-catch","validationCode":"// Verify the account can create objects in the bucket before initiating\n// storage.objects.get on a known key, or check IAM roles/storage.objectCreator","typeGuard":null,"tryCatchPattern":"try { await GoogleCommon.ChunkedUploadWithResumeAsync<...>(...); }\ncatch (HttpRequestException ex) when (ex.Message.Contains(\"Failed to start upload session\"))\n{\n    // status code carries the real cause (404 bucket, 403 perms, 5xx transient)\n    _logger.LogError(ex, \"Upload session init failed with status {Status}\", ex.StatusCode);\n    throw;\n}","preventionTips":["Ensure the bucket exists and the account has storage.objects.create before uploads.","Confirm no proxy strips the Location response header.","Capture the session-init response body on failure for diagnosis."],"tags":["gcs","google-common","resumable-upload","http-headers","session-init","upload"],"backgroundTag":null,"analyzedSha":"3f348be3e33f5d72d414e3ad55839c2ba34dda67","analyzedAt":"2026-08-13T16:48:27.008Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}