{"record":{"id":"0ec9a34438e878f8","repo":"sveltejs/kit","slug":"could-not-deserialize-binary-form-gaps-in-file-da","errorCode":null,"errorMessage":"Could not deserialize binary form: gaps in file data","messagePattern":"Could not deserialize binary form: gaps in file data","errorType":"exception","errorClass":"SvelteKitError","httpStatus":400,"severity":"error","filePath":"packages/kit/src/runtime/form-utils.js","lineNumber":332,"sourceCode":"\t\t\t\t\t// Trick validators into thinking this is a normal File\n\t\t\t\t\treturn File.prototype;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t});\n\n\t// Sort file spans in increasing order primarily by offset\n\t// and secondarily by size (to allow 0-length files).\n\tfile_spans.sort((a, b) => a.offset - b.offset || a.size - b.size);\n\n\t// Check that file spans do not overlap and there are no gaps between them.\n\tfor (let i = 1; i < file_spans.length; i++) {\n\t\tconst previous = file_spans[i - 1];\n\t\tconst current = file_spans[i];\n\n\t\tconst previous_end = previous.offset + previous.size;\n\t\tif (previous_end < current.offset) {\n\t\t\tthrow deserialize_error('gaps in file data');\n\t\t}\n\t\tif (previous_end > current.offset) {\n\t\t\tthrow deserialize_error('overlapping file data');\n\t\t}\n\t}\n\n\t// Read the request body asynchronously so it doesn't stall\n\tvoid (async () => {\n\t\tlet has_more = true;\n\t\twhile (has_more) {\n\t\t\tconst chunk = await get_chunk(chunks.length);\n\t\t\thas_more = !!chunk;\n\t\t}\n\t})().catch(noop); // prevent unhandled rejection potentially crashing the process\n\n\treturn { data, meta, form_data: null };\n}\n/**","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/sveltejs/kit/blob/03f1687fe612ce3d2d9131139b5b188d9cf90c64/packages/kit/src/runtime/form-utils.js#L314-L350","documentation":"In the binary form payload, the declared file regions must exactly tile the files section: each file's end offset must not fall short of the next file's start offset. deserialize_binary_form throws 'gaps in file data' when there are unaccounted bytes between consecutive files, indicating a corrupted or inconsistent payload.","triggerScenarios":"A SvelteKit action upload where file sizes/offsets in the offset table leave holes between files — truncated bodies, wrong 'size' values in metadata, hand-built payloads, or serializer version mismatch.","commonSituations":"Proxy buffering truncation on large multi-file uploads; clients editing payloads; version skew between client serializer and server deserializer; security probing of action endpoints.","solutions":["Verify Content-Length and that no intermediary truncates large uploads.","Keep client and server on the same @sveltejs/kit version.","Use native form POST or use:enhance so serialization is canonical.","Retry the upload; if reproducible, capture the raw body for diagnosis."],"exampleFix":"// before: size not matching actual bytes\noffsets: [0, 100], sizes: [50, 20] // gap between 50 and 100\n// after: contiguous spans\noffsets: [0, 50], sizes: [50, 20]","handlingStrategy":"validation","validationCode":"spans.sort((a, b) => a.offset - b.offset);\nfor (let i = 1; i < spans.length; i++) {\n  if (spans[i - 1].offset + spans[i - 1].size < spans[i].offset) throw new Error('gap in file data');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await deserialize_binary_form(request);\n} catch (e) {\n  if (String(e.message).includes('gaps in file data')) {\n    return new Response('Corrupted upload, please retry', { status: 400 });\n  }\n  throw e;\n}","preventionTips":["Check intermediary body-size limits and buffering on large uploads.","Use canonical kit serialization so sizes/offsets always agree.","Retry failed uploads and log raw body length when reproducible."],"tags":["sveltekit","forms","serialization","integrity"],"backgroundTag":"binary-payload-corruption","analyzedSha":"03f1687fe612ce3d2d9131139b5b188d9cf90c64","analyzedAt":"2026-09-02T02:01:50.504Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}