juicedata/juicefs · error

Decrypt: chunk data truncated: need

Error message

Decrypt: chunk data truncated: need %d, have %d

What it means

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.

Solutions

  1. Restore the truncated object from backup or re-sync it with juicefs sync --check-all
  2. Verify integrity with the storage backend's own checksum tooling (e.g. S3 ETag compare) to identify corrupted objects
  3. Check proxy/gateway configs for request-size truncation between client and object store
  4. If corruption is systemic (many objects), re-create the volume and copy data with encryption enabled end-to-end
Defensive patterns

Strategy: try-catch

Validate before calling

if len(chunk) >= chunkHeaderSize {
	ctLen := int(binary.BigEndian.Uint32(chunk[:4]))
	if chunkHeaderSize+ctLen > len(chunk) { /* truncated chunk body */ }
}

Try / catch

n, err := r.Read(buf)
if err != nil && strings.Contains(err.Error(), "chunk data truncated") {
	// repair: restore object or re-run juicefs sync --check-all
}

Prevention

When it happens

Trigger: 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.

Common situations: Interrupted multipart/partial uploads; partial bucket restore or backup restore; network proxy truncating large GETs; object corruption in the storage backend.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/719785b0bd9ad18f. Report an issue: GitHub.

Appendix: source

Thrown at pkg/object/encrypt_chunked.go:164

	chunkBuf := r.pool.Get().(*[]byte)
	defer func() {
		if len(r.buf) == 0 {
			r.pool.Put(chunkBuf)
		}
	}()

	n, err := io.ReadFull(r.r, *chunkBuf)
	chunk := (*chunkBuf)[:n]
	if err != io.ErrUnexpectedEOF && err != nil {
		return 0, err
	}

	if len(chunk) < chunkHeaderSize {
		return 0, fmt.Errorf("Decrypt: truncated chunk header")
	}
	ctLen := int(binary.BigEndian.Uint32(chunk[:chunkHeaderSize]))
	if chunkHeaderSize+ctLen > len(chunk) {
		return 0, fmt.Errorf("Decrypt: chunk data truncated: need %d, have %d", chunkHeaderSize+ctLen, len(chunk))
	}

	plain, decErr := r.enc.Decrypt(chunk[chunkHeaderSize : chunkHeaderSize+ctLen])
	if decErr != nil {
		return 0, fmt.Errorf("Decrypt: %s", decErr)
	}

	if r.skip > 0 {
		skip := r.skip
		r.skip = 0
		if skip >= int64(len(plain)) {
			return 0, io.EOF
		}
		plain = plain[skip:]
	}

	n = copy(p, plain)
	if n < len(plain) {

View on GitHub (pinned to c9a67b23e8)