{"record":{"id":"719785b0bd9ad18f","repo":"juicedata/juicefs","slug":"decrypt-chunk-data-truncated-need-d-have-d","errorCode":null,"errorMessage":"Decrypt: chunk data truncated: need %d, have %d","messagePattern":"Decrypt: chunk data truncated: need (.+?), have (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/object/encrypt_chunked.go","lineNumber":164,"sourceCode":"\tchunkBuf := r.pool.Get().(*[]byte)\n\tdefer func() {\n\t\tif len(r.buf) == 0 {\n\t\t\tr.pool.Put(chunkBuf)\n\t\t}\n\t}()\n\n\tn, err := io.ReadFull(r.r, *chunkBuf)\n\tchunk := (*chunkBuf)[:n]\n\tif err != io.ErrUnexpectedEOF && err != nil {\n\t\treturn 0, err\n\t}\n\n\tif len(chunk) < chunkHeaderSize {\n\t\treturn 0, fmt.Errorf(\"Decrypt: truncated chunk header\")\n\t}\n\tctLen := int(binary.BigEndian.Uint32(chunk[:chunkHeaderSize]))\n\tif chunkHeaderSize+ctLen > len(chunk) {\n\t\treturn 0, fmt.Errorf(\"Decrypt: chunk data truncated: need %d, have %d\", chunkHeaderSize+ctLen, len(chunk))\n\t}\n\n\tplain, decErr := r.enc.Decrypt(chunk[chunkHeaderSize : chunkHeaderSize+ctLen])\n\tif decErr != nil {\n\t\treturn 0, fmt.Errorf(\"Decrypt: %s\", decErr)\n\t}\n\n\tif r.skip > 0 {\n\t\tskip := r.skip\n\t\tr.skip = 0\n\t\tif skip >= int64(len(plain)) {\n\t\t\treturn 0, io.EOF\n\t\t}\n\t\tplain = plain[skip:]\n\t}\n\n\tn = copy(p, plain)\n\tif n < len(plain) {","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/object/encrypt_chunked.go#L146-L182","documentation":"Each chunked ciphertext record declares its length in the 4-byte header. If header+ctLen exceeds the bytes actually read, the chunk body is incomplete and the reader reports how many bytes were needed versus available.","triggerScenarios":"Calling Read on a chunkedEncryptedObject when a chunk's body is cut short — the read returned fewer bytes than chunkHeaderSize+ctLen (e.g. last chunk partially uploaded) or the header was corrupted to a bogus ctLen.","commonSituations":"Interrupted multipart/partial uploads; partial bucket restore or backup restore; network proxy truncating large GETs; object corruption in the storage backend.","solutions":["Restore the truncated object from backup or re-sync it with juicefs sync --check-all","Verify integrity with the storage backend's own checksum tooling (e.g. S3 ETag compare) to identify corrupted objects","Check proxy/gateway configs for request-size truncation between client and object store","If corruption is systemic (many objects), re-create the volume and copy data with encryption enabled end-to-end"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if len(chunk) >= chunkHeaderSize {\n\tctLen := int(binary.BigEndian.Uint32(chunk[:4]))\n\tif chunkHeaderSize+ctLen > len(chunk) { /* truncated chunk body */ }\n}","typeGuard":null,"tryCatchPattern":"n, err := r.Read(buf)\nif err != nil && strings.Contains(err.Error(), \"chunk data truncated\") {\n\t// repair: restore object or re-run juicefs sync --check-all\n}","preventionTips":["Use --check-all syncs after restores to validate chunk layouts","Enable storage-side integrity checks (ETag/CRC) on writes","Avoid proxies with response-size limits between client and store","Keep object writes atomic; prefer complete multipart finalization"],"tags":["crypto","decrypt","truncated-data","object-storage"],"backgroundTag":"checksum-mismatch","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}