{"record":{"id":"7e7e65db1b68f893","repo":"schollz/croc","slug":"storage-service-returned-unsupported-chunk-size","errorCode":null,"errorMessage":"Storage service returned unsupported chunk size ${created.chunkSize}","messagePattern":"Storage service returned unsupported chunk size (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"web/src/protocol/stored.ts","lineNumber":440,"sourceCode":"      protocol: storedProtocol,\n      manifestBytes: plan.manifestJSON.byteLength + 28,\n      chunkBytes: plan.chunkBytes,\n      redeemVerifier: base64URL(redeemVerifier),\n      files: plan.files.length,\n      plaintextBytes: plan.totalSize,\n      ...(downloads === 1 ? {} : { downloads }),\n      ...(expiresSeconds === 24 * 60 * 60 ? {} : { expiresSeconds }),\n    }),\n  });\n  if (!response.ok) throw await responseError(response);\n  const created = (await response.json()) as {\n    id: string;\n    uploadToken: string;\n    uploadExpiresAt: string;\n    chunkSize: number;\n  };\n  if (created.chunkSize !== storedChunkSize) {\n    throw new Error(\n      `Storage service returned unsupported chunk size ${created.chunkSize}`,\n    );\n  }\n  if (!isCapability(created.uploadToken)) {\n    throw new Error(\"Storage service returned an invalid upload capability\");\n  }\n  const downloadsHeader = response.headers.get(\"X-Croc-Downloads\");\n  const acceptedDownloads =\n    downloadsHeader === null ? 1 : Number(downloadsHeader);\n  if (!Number.isSafeInteger(acceptedDownloads) || acceptedDownloads < 1) {\n    throw new Error(\"Storage service returned an invalid download count\");\n  }\n  if (acceptedDownloads !== downloads) {\n    throw new Error(\n      `Storage service created ${acceptedDownloads} downloads instead of ${downloads}`,\n    );\n  }\n  return {","sourceCodeStart":422,"sourceCodeEnd":458,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/stored.ts#L422-L458","documentation":"After POSTing a new stored transfer, createStoredUpload() verifies the server's declared chunkSize equals the client's storedChunkSize (4 MiB). Chunk encryption (storeSealChunk) binds each chunk to its index and size, so a server with a different chunk size would produce ciphertext the receiving client cannot reassemble; the client aborts immediately instead of uploading garbage.","triggerScenarios":"The create response JSON has chunkSize !== 4194304 — typically a version skew: an older/newer storage service that chunks differently, or a misconfigured third-party implementation of the /transfers API.","commonSituations":"Deploying a new web client against an old storeAPI deployment (or vice versa) during a rolling upgrade; pointing storeAPI at an incompatible self-hosted relay.","solutions":["Update the storage service and web client to matching versions (both must agree on the 4 MiB chunk size)","Check settings.storeAPI points at the intended service, not a stale or third-party endpoint","If you implement the service yourself, echo chunkSize: 4194304 in the create response"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { return await createStoredUpload(...); } catch (e) { if (e instanceof Error && e.message.includes(\"chunk size\")) { alert(\"Client/server version mismatch — update the app and storage service\"); } throw e; }","preventionTips":["Deploy web client and storage service in lockstep so storedChunkSize (4 MiB) always matches","Pin storeAPI to the version-matched deployment during rolling upgrades"],"tags":["protocol","version-skew","stored-transfer","server-contract"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}