Repository navigation
fix(transcription): send OpenAI uploads through requestUrl - #514
Merged
Merged
Conversation
The transcription tests now answer OpenAI through Obsidian's requestUrl and reject fetch the way Chromium does when OpenAI's response has no Access-Control-Allow-Origin header for app://obsidian.md. A plain transcription that fails on every chunk must show the last API error, and requestWithTimeout must accept the Set-Cookie array that the desktop requestUrl returns. The e2e journeys stub OpenAI on the request-url IPC channel instead of fetch. These tests fail until the next commit.
OpenAI answers errors without an Access-Control-Allow-Origin header for app://obsidian.md. Chromium blocked those responses from the renderer's fetch, so every failed upload, plain or diarized, showed "Connection error." and hid the real cause, such as a bad key or no quota. createTranscription now posts the same multipart bytes through requestWithTimeout and accepts every status, so OpenAI's message and Retry-After reach the caller. A timeout or abort stops waiting and drops the late response, because requestUrl cannot cancel an upload. requestWithTimeout rejected every response that set a cookie, because the desktop requestUrl returns Set-Cookie as an array. It now joins string arrays with ", ". A plain transcription that fails on every chunk now ends its notice with the last chunk's error and no longer repeats "Transcription failed:". The verify skill's fixture provider now answers OpenAI on requestUrl.
Deploying podnotes with
|
| Latest commit: |
e4b6bc9
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://56386c4b.podnotes.pages.dev |
| Branch Preview URL: | https://fix-transcription-cors.podnotes.pages.dev |
|
Hi Christian, Thanks for the quick fix and your great work on PodNotes! I really appreciate it and look forward to testing the next release. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
On desktop, every OpenAI transcription, plain or diarized, failed with "Connection error." OpenAI sends its error responses without an
Access-Control-Allow-Originheader forapp://obsidian.md. Chromium blocked them from the renderer'sfetch, so users never saw the real cause, such as a bad key or no quota.What changed
createTranscriptionposts the same multipart bytes throughrequestWithTimeout, which uses Obsidian'srequestUrland has no CORS check. It accepts every status, so OpenAI's message andRetry-Afterreach the existing error mapping and retries.requestWithTimeoutjoins a header thatrequestUrlreturns as an array. Desktop returnsSet-Cookiethat way, and OpenAI always sets a Cloudflare cookie, so the wrapper rejected every OpenAI response as "Network response is invalid."Scope
requestUrlagainst the verify skill's fixture provider and the e2e journey stub.requestUrlcannot cancel an upload. Cancel and the 10-minute timeout stop waiting and drop the late response, as streaming downloads do. The retry after a timeout still starts while the first upload runs. That is left for a follow-up.Blast Radius
OpenAI transcription and diarization on every platform. The header change also reaches episode downloads. On desktop, a download from a host that sets a cookie should fail today with "Network response is invalid.", and this fixes it. That is inferred from the code path, not reproduced.
Verification
Transcribe current episode from audioon a 60 s local WAV. Before, the plain notice readTranscription failed: Transcription failed: all 1 audio chunk(s) failed or returned no text., the diarized one ended inConnection error., and DevTools logged the CORS block on each of the three sends. After, both notices end with401 Incorrect API key provided: sk-trx51*********-key. ..., and DevTools logs nothing.npm run test:e2e -- tests/e2e/journey-resilience.test.ts -t "J-TR-0[234]"passes. J-TR-02 checks the SHA-256 of the file parsed back from therequestUrlbody.npm run lint,format:check,typecheck,build,test(2,782 tests),docs:buildandtest:toolingpass.npm run sizereports 101,575 bytes gzip, against 101,457 on master and a budget of 103,000.Each of these mutants fails at least one test:
fetchclient: 29 of 34 client and provider tests.acceptedStatuses, the 2xx-only default that Deepgram uses: the 401, 429, 500, 502 and 503 tests.Set-Cookiearrays still rejected: therequestWithTimeoutSet-Cookie test.Docs at release
None.
Fixes #513
Note
High Risk
Changes the network path for all OpenAI transcription and diarization plus shared
requestWithTimeoutheader handling, affecting paid transcription on every platform and episode downloads that receive array Set-Cookie headers.Overview
Fixes desktop OpenAI transcription failing with a generic "Connection error." by routing Whisper and diarized uploads through Obsidian's
requestUrl(viarequestWithTimeout) instead of rendererfetch, so CORS-blocked responses and real API errors (401, 429, etc.) surface correctly.copyResponseHeadersnow joinsSet-Cookiewhen Obsidian returns it as a string array, which previously made every OpenAI response invalid.createTranscriptionaccepts all HTTP statuses for manual error mapping and keeps retry/Retry-Afterbehavior.When every audio chunk fails, failure notices append the last chunk error and drop the duplicated "Transcription failed:" prefix. Tests, e2e journey stubs, and the verify
net.mjsfixture were updated to mockrequestUrlinstead offetchfor OpenAI.Reviewed by Cursor Bugbot for commit e4b6bc9. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Route OpenAI transcription uploads through
requestUrlincreateTranscriptionfetchupload in openaiTranscription.ts withrequestWithTimeout, enforcing the 25 MiB body limit and a ten-minute timeout, and decoding binary responses before parsing JSONTranscriptionService.transcribeChunksnow tracks the final chunk error sotranscribeEpisodereports the last failure detail when no usable transcript is producedcopyResponseHeadersin networkRequest.ts now accepts array-shaped string headers (e.g.Set-Cookie), joining values with commasrequestUrlmock (openaiRequestUrl.ts) instead offetchrequestUrlcannot cancel an in-flight upload, and abort tests no longer assert a cancelled fetch signal. Failure messages for exhausted chunks now include the last error detailMacroscope summarized e4b6bc9.