{"record":{"id":"605e249b5ea46046","repo":"schollz/croc","slug":"received-more-data-than-the-advertised-file-size","errorCode":null,"errorMessage":"Received more data than the advertised file size","messagePattern":"Received more data than the advertised file size","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/protocol/client.ts","lineNumber":634,"sourceCode":"      if (active.received.has(position)) throw new Error(\"Received a duplicate file chunk\");\n      if (\n        position < 0 ||\n        position % CHUNK_SIZE !== 0 ||\n        bytes.byteLength === 0 ||\n        bytes.byteLength > CHUNK_SIZE ||\n        position + bytes.byteLength > active.file.size\n      ) {\n        throw new Error(\"Received a file chunk outside the advertised file size\");\n      }\n      active.received.add(position);\n      await active.sink.writeAt(position, bytes);\n      active.bytes += bytes.byteLength;\n      active.progress(active.bytes);\n      if (active.bytes === active.file.size) {\n        this.active = undefined;\n        active.resolve();\n      } else if (active.bytes > active.file.size) {\n        throw new Error(\"Received more data than the advertised file size\");\n      }\n    });\n    active.queue.catch((error) => {\n      if (this.active === active) this.active = undefined;\n      active.reject(error instanceof Error ? error : new Error(String(error)));\n    });\n    return active.queue;\n  }\n}\n\nexport async function receiveFiles(options: {\n  secret: string;\n  settings: TransferSettings;\n  callbacks: ReceiveCallbacks;\n  signal?: AbortSignal;\n}) {\n  const { secret, settings, callbacks, signal } = options;\n  validateSecret(secret);","sourceCodeStart":616,"sourceCodeEnd":652,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/client.ts#L616-L652","documentation":"Thrown inside DataReceiver.accept when the accumulated byte count for the active file exceeds the advertised size. The aligned-position check should prevent overshoot, so reaching this branch means total received bytes (counting every accepted chunk) went past file.size — e.g. overlapping-but-not-duplicate offsets after rounding, or a size advertisement smaller than the true file. It is a final consistency backstop that fails the transfer before finalize/verify.","triggerScenarios":"Sender advertises size N in fileinfo but streams more than N bytes (file grew after hashing); rounding in chunkCount math (Math.ceil(size/CHUNK_SIZE)) sending a full final chunk where a partial one was expected and position checks passed; a peer sending an extra chunk past the final aligned offset with distinct positions.","commonSituations":"The sender's file is appended to (log file, download in progress) between prepareFiles and the transfer loop; inconsistent size fields between senderInfo and sendFileData; adversarial sender probing the receiver's bounds checks.","solutions":["Guarantee the file is immutable from hashing through streaming; snapshot large/active files before prepareFiles","Cross-check prepared.size against the actual bytes sliced in sendFileData in tests","Treat this error as evidence of sender-side state drift — re-prepare files and restart the transfer","If it reproduces deterministically at file end, log chunkCount, final position, and advertised size to find the off-by-one"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Sender-side: snapshot mutable files before hashing so size cannot drift\nconst snapshot = await file.slice(0, file.size).arrayBuffer(); // then hash/stream from the snapshot","typeGuard":null,"tryCatchPattern":"catch (e) {\n  if (e instanceof Error && e.message === \"Received more data than the advertised file size\") {\n    // sender streamed past its own advertisement: re-prepare files and restart\n  }\n  throw e;\n}","preventionTips":["Do not transfer files that are actively being written; copy them first and transfer the copy","Re-run prepareFiles whenever the source File objects may have changed","On this error always restart from prepareFiles — partial state is unreliable"],"tags":["data-integrity","receiver","validation"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}