{"record":{"id":"c37a6b5b689069f3","repo":"SubtitleEdit/subtitleedit","slug":"dashscope-upload-policy-response-could-not-be-pars","errorCode":null,"errorMessage":"DashScope upload-policy response could not be parsed. Response: {policyJson}","messagePattern":"DashScope upload-policy response could not be parsed\\. Response: (.+?)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/SpeechToText/DashScope/DashScopeSttService.cs","lineNumber":132,"sourceCode":"        var policyUrl = $\"{BaseUrl}{UploadsPath}?action=getPolicy&model={Uri.EscapeDataString(model)}\";\n        using var policyRequest = new HttpRequestMessage(HttpMethod.Get, policyUrl);\n        policyRequest.Headers.Authorization = new AuthenticationHeaderValue(\"Bearer\", _settings.ApiKey);\n\n        using var policyResponse = await _httpClient.SendAsync(policyRequest, HttpCompletionOption.ResponseHeadersRead, ct);\n        if (!policyResponse.IsSuccessStatusCode)\n        {\n            var err = await policyResponse.Content.ReadAsStringAsync(ct);\n            _settings.Logger?.Invoke($\"DashScope upload-policy request failed: GET {policyUrl} => {(int)policyResponse.StatusCode}: {err}\");\n            throw new HttpRequestException($\"DashScope upload-policy request failed ({(int)policyResponse.StatusCode}). Response: {err}\");\n        }\n\n        var policyJson = await policyResponse.Content.ReadAsStringAsync(ct);\n        var policy = JsonSerializer.Deserialize<DashScopeUploadPolicyResponse>(policyJson,\n            new JsonSerializerOptions { PropertyNameCaseInsensitive = true })?.Data;\n        if (policy == null)\n        {\n            _settings.Logger?.Invoke($\"DashScope upload-policy response could not be parsed: {policyJson}\");\n            throw new InvalidOperationException($\"DashScope upload-policy response could not be parsed. Response: {policyJson}\");\n        }\n\n        var key = $\"{policy.UploadDir}/{fileName}\";\n\n        using var form = new MultipartFormDataContent\n        {\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","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/SpeechToText/DashScope/DashScopeSttService.cs#L114-L150","documentation":"Thrown when the upload-policy endpoint returns HTTP 200 but the JSON body cannot be deserialized into a DashScopeUploadPolicyResponse with a non-null Data property. This means the API changed its response schema, returned an unexpected envelope, or sent HTML instead of JSON (e.g. a CDN error page).","triggerScenarios":"The response body is valid JSON but the structure doesn't match DashScopeUploadPolicyResponse (e.g. Data is nested differently or uses different property names); the response is not JSON at all (HTML error page with 200 status from a proxy); the response is a JSON error object without a Data field.","commonSituations":"DashScope updated their API response schema without notice; a transparent proxy or CDN returns a 200 HTML page; the API key is valid but the account lacks upload permissions, returning a JSON error without Data; network middleware rewrites the response body.","solutions":["Examine the raw policyJson included in the error message to see the actual response structure.","If the JSON structure changed, update the DashScopeUploadPolicyResponse class to match the new schema.","If the response is HTML, investigate proxy/CDN interference in the network path.","Verify the API key has upload/storage permissions in the DashScope console.","Check if PropertyNameCaseInsensitive is handling the property names correctly — compare the JSON keys against the C# property names."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-validate response is JSON before deserializing\nvar contentType = policyResponse.Content.Headers.ContentType?.MediaType;\nif (contentType != \"application/json\")\n    throw new InvalidOperationException($\"Expected JSON from upload-policy endpoint, got {contentType}.\");","typeGuard":null,"tryCatchPattern":"try { var policy = JsonSerializer.Deserialize<DashScopeUploadPolicyResponse>(policyJson, opts)?.Data; }\ncatch (JsonException ex)\n{ /* log raw JSON, check for schema change, surface actionable error */ }","preventionTips":["Check the Content-Type header is application/json before deserialization.","Log the raw response body for any deserialization failure to aid schema debugging.","Pin to a known DashScope API version if versioned endpoints are available.","Monitor DashScope API changelog for response schema updates."],"tags":["deserialization","dashscope","speech-to-text","schema-mismatch"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}