Both places where the SDK reads Cryptify's rolling upload token cast a possibly-null header to string:
src/api/cryptify.ts:122 — initUpload: const token = response.headers.get('cryptifytoken') as string;
src/api/cryptify.ts:222 — storeChunk: same cast
Headers.get() returns null when the header is absent. The most likely way that happens in a browser is a Cryptify deployment (or a proxy in front of it) that doesn't list cryptifytoken in Access-Control-Expose-Headers — the response is a 200, but the header is invisible to the SDK.
What the caller sees today: state.token becomes null, the next chunk PUT sends cryptifytoken: null as a literal string, and Cryptify rejects it with a token mismatch. The surfaced error points at chunk upload, several steps away from the actual cause. The same silent null can also be written into a FileState the consumer persists for resumeUpload.
Fix: after each headers.get('cryptifytoken'), throw a NetworkError naming the missing header and the CORS-exposure cause instead of casting. Add unit tests in tests/api.test.ts for a 200 init response and a 200 chunk response that carry no cryptifytoken header.
Both places where the SDK reads Cryptify's rolling upload token cast a possibly-
nullheader tostring:src/api/cryptify.ts:122—initUpload:const token = response.headers.get('cryptifytoken') as string;src/api/cryptify.ts:222—storeChunk: same castHeaders.get()returnsnullwhen the header is absent. The most likely way that happens in a browser is a Cryptify deployment (or a proxy in front of it) that doesn't listcryptifytokeninAccess-Control-Expose-Headers— the response is a 200, but the header is invisible to the SDK.What the caller sees today:
state.tokenbecomesnull, the next chunk PUT sendscryptifytoken: nullas a literal string, and Cryptify rejects it with a token mismatch. The surfaced error points at chunk upload, several steps away from the actual cause. The same silentnullcan also be written into aFileStatethe consumer persists forresumeUpload.Fix: after each
headers.get('cryptifytoken'), throw aNetworkErrornaming the missing header and the CORS-exposure cause instead of casting. Add unit tests intests/api.test.tsfor a 200 init response and a 200 chunk response that carry nocryptifytokenheader.