{"record":{"id":"2f26449354ce1ec8","repo":"juicedata/juicefs","slug":"write-out-of-chunk-boundary-d-d","errorCode":null,"errorMessage":"write out of chunk boundary: %d > %d","messagePattern":"write out of chunk boundary: (.+?) > (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/chunk/cached_store.go","lineNumber":257,"sourceCode":"\t\trSlice:    rSlice{id, 0, store},\n\t\tpages:     make([][]*Page, chunkSize/store.conf.BlockSize),\n\t\terrors:    make(chan error, chunkSize/store.conf.BlockSize),\n\t\twriteback: store.conf.Writeback,\n\t\ttierID:    tierID,\n\t}\n}\n\nfunc (s *wSlice) SetID(id uint64) {\n\ts.id = id\n}\n\nfunc (s *wSlice) SetWriteback(enabled bool) {\n\ts.writeback = enabled\n}\n\nfunc (s *wSlice) WriteAt(p []byte, off int64) (n int, err error) {\n\tif int(off)+len(p) > chunkSize {\n\t\treturn 0, fmt.Errorf(\"write out of chunk boundary: %d > %d\", int(off)+len(p), chunkSize)\n\t}\n\tif off < int64(s.uploaded) {\n\t\treturn 0, fmt.Errorf(\"Cannot overwrite uploaded block: %d < %d\", off, s.uploaded)\n\t}\n\n\t// Fill previous blocks with zeros\n\tif s.length < int(off) {\n\t\tzeros := make([]byte, int(off)-s.length)\n\t\t_, _ = s.WriteAt(zeros, int64(s.length))\n\t}\n\n\tfor n < len(p) {\n\t\tindx := s.index(int(off) + n)\n\t\tboff := (int(off) + n) % s.store.conf.BlockSize\n\t\tvar bs = pageSize\n\t\tif indx > 0 || bs > s.store.conf.BlockSize {\n\t\t\tbs = s.store.conf.BlockSize\n\t\t}","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/chunk/cached_store.go#L239-L275","documentation":"wSlice represents one slice of a chunk (chunkSize bytes) being staged for upload. Its WriteAt enforces that writes stay within the fixed chunk boundary; a write whose offset+length exceeds chunkSize is rejected. This guards the internal invariant that one wSlice maps to at most chunkSize bytes.","triggerScenarios":"Calling wSlice.WriteAt with p and off such that int(off)+len(p) > chunkSize — e.g. a caller splitting data into chunks incorrectly, or passing an offset near the end of a chunk with a buffer sized for a full block.","commonSituations":"Custom tooling writing to the chunk store directly with miscomputed block offsets; internal bugs in chunk splitting after a version change of chunkSize assumptions; replaying a write log with wrong offsets.","solutions":["Fix the caller to split writes so each write fits within chunkSize (end at off+len(p) <= chunkSize).","Verify chunkSize used by the caller matches the store's configured BlockSize/chunkSize.","Clamp or split oversized writes across multiple wSlice/chunk boundaries before calling WriteAt.","If this arises during normal mounting (not custom code), report a bug with the workload reproducing it — it indicates an internal chunk-splitting defect."],"exampleFix":"// before\nslice.WriteAt(p, off) // off+len(p) may exceed chunkSize\n// after\nif int(off)+len(p) > chunkSize {\n    n1 := chunkSize - int(off)\n    slice.WriteAt(p[:n1], off)\n    nextSlice.WriteAt(p[n1:], 0)\n}","handlingStrategy":"validation","validationCode":"if int(off)+len(p) > chunkSize {\n    return errors.New(\"write exceeds chunk boundary; split before calling WriteAt\")\n}","typeGuard":null,"tryCatchPattern":"n, err := slice.WriteAt(p, off)\nif err != nil && strings.Contains(err.Error(), \"out of chunk boundary\") {\n    // split the write at chunkSize-off and continue on next slice\n}","preventionTips":["Compute block splits from the store's chunkSize, not hardcoded sizes","Unit-test write paths near chunk boundaries"],"tags":["boundary","write","chunk"],"backgroundTag":"value-out-of-range","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"}