SubtitleEdit/subtitleedit · error · HttpRequestException

DashScope OSS upload failed ({(int)uploadResponse.StatusCode

Error message

DashScope OSS upload failed ({(int)uploadResponse.StatusCode}). Response: {err}

What it means

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.

Source

Thrown at src/ui/Features/Video/SpeechToText/DashScope/DashScopeSttService.cs:156

        {
            { new StringContent(policy.OssAccessKeyId), "OSSAccessKeyId" },
            { new StringContent(policy.Policy), "policy" },
            { new StringContent(policy.Signature), "Signature" },
            { new StringContent(key), "key" },
            { new StringContent(policy.XOssObjectAcl), "x-oss-object-acl" },
            { new StringContent(policy.XOssForbidOverwrite), "x-oss-forbid-overwrite" },
            { new StringContent("200"), "success_action_status" },
        };
        var fileContent = new ByteArrayContent(audioBytes);
        fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        form.Add(fileContent, "file", fileName); // file field must be last

        using var uploadResponse = await _httpClient.PostAsync(policy.UploadHost, form, ct);
        if (!uploadResponse.IsSuccessStatusCode)
        {
            var err = await uploadResponse.Content.ReadAsStringAsync(ct);
            _settings.Logger?.Invoke($"DashScope OSS upload failed: POST {policy.UploadHost} => {(int)uploadResponse.StatusCode}: {err}");
            throw new HttpRequestException($"DashScope OSS upload failed ({(int)uploadResponse.StatusCode}). Response: {err}");
        }

        return "oss://" + key;
    }

    private async Task<string> SubmitTaskAsync(string ossUrl, string? language, CancellationToken ct)
    {
        var body = BuildSubmitBody(_settings, ossUrl, language);
        using var content = new StringContent(body, Encoding.UTF8, "application/json");
        using var request = new HttpRequestMessage(HttpMethod.Post, BaseUrl + TranscriptionPath)
        {
            Content = content,
        };
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _settings.ApiKey);
        request.Headers.TryAddWithoutValidation("X-DashScope-Async", "enable");
        request.Headers.TryAddWithoutValidation("X-DashScope-OssResourceResolve", "enable");

        using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Check the OSS status code: 403 means signature/policy mismatch (often clock skew — sync system time); 413 means file too large.
  2. Ensure the system clock is accurate (NTP sync) — OSS signatures are time-sensitive.
  3. If the file is large, verify the policy doesn't impose a size limit below the file size.
  4. Retry the entire flow (getPolicy + upload) if the error is transient (5xx from OSS).
  5. Inspect the err body in the message for OSS-specific error codes (e.g. InvalidAccessKeyId, SignatureDoesNotMatch).
Defensive patterns

Strategy: retry

Validate before calling

// Ensure system clock is synced before upload (clock skew invalidates OSS signatures)
var ntpOffset = GetNtpOffset();
if (Math.Abs(ntpOffset.TotalSeconds) > 30)
    throw new InvalidOperationException("System clock is off by more than 30 seconds; OSS signatures will be rejected.");

Try / catch

try { await UploadToOss(policy, audioBytes, ct); }
catch (HttpRequestException ex) when (ex.Message.Contains("OSS upload failed"))
{
    var code = ExtractStatusCode(ex.Message);
    if (code == 403) /* re-fetch policy (clock skew / expiry), retry */;
    else if (code >= 500) /* transient OSS error, retry with backoff */;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13). Data as JSON: /api/errors/52a92a235f884622. Report an issue: GitHub.