{"record":{"id":"52a92a235f884622","repo":"SubtitleEdit/subtitleedit","slug":"dashscope-oss-upload-failed-int-uploadresponse","errorCode":null,"errorMessage":"DashScope OSS upload failed ({(int)uploadResponse.StatusCode}). Response: {err}","messagePattern":"DashScope OSS upload failed \\((.+?)\\)\\. Response: (.+?)","errorType":"http","errorClass":"HttpRequestException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/SpeechToText/DashScope/DashScopeSttService.cs","lineNumber":156,"sourceCode":"        {\n            { new StringContent(policy.OssAccessKeyId), \"OSSAccessKeyId\" },\n            { new StringContent(policy.Policy), \"policy\" },\n            { new StringContent(policy.Signature), \"Signature\" },\n            { new StringContent(key), \"key\" },\n            { new StringContent(policy.XOssObjectAcl), \"x-oss-object-acl\" },\n            { new StringContent(policy.XOssForbidOverwrite), \"x-oss-forbid-overwrite\" },\n            { new StringContent(\"200\"), \"success_action_status\" },\n        };\n        var fileContent = new ByteArrayContent(audioBytes);\n        fileContent.Headers.ContentType = new MediaTypeHeaderValue(\"application/octet-stream\");\n        form.Add(fileContent, \"file\", fileName); // file field must be last\n\n        using var uploadResponse = await _httpClient.PostAsync(policy.UploadHost, form, ct);\n        if (!uploadResponse.IsSuccessStatusCode)\n        {\n            var err = await uploadResponse.Content.ReadAsStringAsync(ct);\n            _settings.Logger?.Invoke($\"DashScope OSS upload failed: POST {policy.UploadHost} => {(int)uploadResponse.StatusCode}: {err}\");\n            throw new HttpRequestException($\"DashScope OSS upload failed ({(int)uploadResponse.StatusCode}). Response: {err}\");\n        }\n\n        return \"oss://\" + key;\n    }\n\n    private async Task<string> SubmitTaskAsync(string ossUrl, string? language, CancellationToken ct)\n    {\n        var body = BuildSubmitBody(_settings, ossUrl, language);\n        using var content = new StringContent(body, Encoding.UTF8, \"application/json\");\n        using var request = new HttpRequestMessage(HttpMethod.Post, BaseUrl + TranscriptionPath)\n        {\n            Content = content,\n        };\n        request.Headers.Authorization = new AuthenticationHeaderValue(\"Bearer\", _settings.ApiKey);\n        request.Headers.TryAddWithoutValidation(\"X-DashScope-Async\", \"enable\");\n        request.Headers.TryAddWithoutValidation(\"X-DashScope-OssResourceResolve\", \"enable\");\n\n        using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/SpeechToText/DashScope/DashScopeSttService.cs#L138-L174","documentation":"Thrown when the multipart form POST to the OSS UploadHost (Alibaba Object Storage) returns a non-success HTTP status. This is the second step: after getting the upload policy, the audio bytes are POSTed directly to OSS using the signed form fields. The status code and OSS error response are included.","triggerScenarios":"The signed policy/expiry has lapsed between getPolicy and the POST (clock skew); the file content exceeds the policy's size limit; the OSS bucket ACL or signature is invalid; the OSS host rejects the multipart encoding; the upload dir in the policy is wrong.","commonSituations":"Clock skew between client and OSS server causes the signed policy to be expired on arrival; large audio files exceed the policy's content-length limit; the API key's associated OSS bucket is misconfigured; transient OSS errors during peak load.","solutions":["Check the OSS status code: 403 means signature/policy mismatch (often clock skew — sync system time); 413 means file too large.","Ensure the system clock is accurate (NTP sync) — OSS signatures are time-sensitive.","If the file is large, verify the policy doesn't impose a size limit below the file size.","Retry the entire flow (getPolicy + upload) if the error is transient (5xx from OSS).","Inspect the err body in the message for OSS-specific error codes (e.g. InvalidAccessKeyId, SignatureDoesNotMatch)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Ensure system clock is synced before upload (clock skew invalidates OSS signatures)\nvar ntpOffset = GetNtpOffset();\nif (Math.Abs(ntpOffset.TotalSeconds) > 30)\n    throw new InvalidOperationException(\"System clock is off by more than 30 seconds; OSS signatures will be rejected.\");","typeGuard":null,"tryCatchPattern":"try { await UploadToOss(policy, audioBytes, ct); }\ncatch (HttpRequestException ex) when (ex.Message.Contains(\"OSS upload failed\"))\n{\n    var code = ExtractStatusCode(ex.Message);\n    if (code == 403) /* re-fetch policy (clock skew / expiry), retry */;\n    else if (code >= 500) /* transient OSS error, retry with backoff */;\n}","preventionTips":["Ensure the system clock is NTP-synced — OSS signed policies are time-sensitive.","Minimize the time between getPolicy and the OSS POST to avoid policy expiry.","Verify audio file size is within the policy's content-length limit.","Implement retry logic for transient OSS 5xx errors."],"tags":["network","http","oss","dashscope","speech-to-text","upload"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}