{"record":{"id":"621bd8fb0530abf5","repo":"juicedata/juicefs","slug":"handler-not-set","errorCode":null,"errorMessage":"handler not set","messagePattern":"handler not set","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/vfs/fill.go","lineNumber":442,"sourceCode":"\t}\n\n\tvar parts []chunk.Range\n\tfor _, r := range iter.ranges {\n\t\tstart := max(r.Start, sliceStart)\n\t\tend := min(r.End, sliceStart+uint64(s.Len))\n\t\tif start < end {\n\t\t\tparts = append(parts, chunk.Range{\n\t\t\t\tOff: s.Off + uint32(start-sliceStart),\n\t\t\t\tLen: uint32(end - start),\n\t\t\t})\n\t\t}\n\t}\n\treturn s, parts\n}\n\nfunc (iter *sliceIterator) Iterate(handler sliceHandler, concurrent chan token) error {\n\tif handler == nil {\n\t\treturn fmt.Errorf(\"handler not set\")\n\t}\n\tvar wg sync.WaitGroup\n\tfor iter.hasNext() {\n\t\ts, parts := iter.next()\n\t\tif len(parts) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tvar bytes uint64\n\t\tfor _, p := range parts {\n\t\t\tbytes += uint64(p.Len)\n\t\t}\n\t\tatomic.AddUint64(&iter.stat.SliceCount, 1)\n\t\tatomic.AddUint64(&iter.stat.TotalBytes, bytes)\n\n\t\tselect {\n\t\tcase concurrent <- token{}:\n\t\t\twg.Add(1)\n\t\t\tgo func() {","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/vfs/fill.go#L424-L460","documentation":"sliceIterator.Iterate in pkg/vfs/fill.go requires a non-nil sliceHandler callback to receive each slice and its parts as it walks the chunk slices of an inode. If the caller passes nil, the iterator refuses to start because there would be nowhere to deliver results, returning \"handler not set\". It is a pure programmer-error guard, not a runtime/environment failure.","triggerScenarios":"Calling Iterate(nil, concurrent) on a *sliceIterator, e.g. when wrapping fillCache/fill chunk iteration and forwarding a nil callback, or when a struct field holding the handler was never initialized before the iterate call.","commonSituations":"Refactoring the cache-fill path where the handler is conditionally assigned; calling Iterate from a helper that takes an optional handler parameter; wiring VFS internal callbacks asynchronously so the field is still nil at call time.","solutions":["Pass a non-nil sliceHandler implementation to Iterate.","If the handler is legitimately optional, return early before calling Iterate instead of passing nil.","Check the variable that supplies the handler for a typo/uninitialized field assignment."],"exampleFix":"// before\nerr := iter.Iterate(nil, concurrent)\n// after\nif handler == nil {\n    return errors.New(\"no slice handler provided\")\n}\nerr := iter.Iterate(handler, concurrent)","handlingStrategy":"validation","validationCode":"if handler == nil {\n    return errors.New(\"slice handler must be provided\")\n}\nerr := iter.Iterate(handler, concurrent)","typeGuard":"func handlerSet(h sliceHandler) bool { return h != nil }","tryCatchPattern":"if err := iter.Iterate(handler, concurrent); err != nil {\n    if strings.Contains(err.Error(), \"handler not set\") { /* fix caller */ }\n    return err\n}","preventionTips":["Never pass nil callbacks to iterator-style APIs.","Make handler parameters non-optional in wrapper functions.","Initialize handler fields in constructors, not lazily."],"tags":["nil-argument","go","vfs"],"backgroundTag":"null-argument","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"}