{"record":{"id":"5d7367ecbaa02f86","repo":"cilium/cilium","slug":"errmapnotopened","errorCode":"ErrMapNotOpened","errorMessage":"%s: %w","messagePattern":"%s: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/bpf/map_linux.go","lineNumber":1203,"sourceCode":"\n\tif err := m.DumpWithCallback(callback); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// BatchLookup returns the count of elements in the map by dumping the map\n// using batch lookup.\nfunc (m *Map) BatchLookup(cursor *ebpf.MapBatchCursor, keysOut, valuesOut any, opts *ebpf.BatchOptions) (int, error) {\n\t// Hold the read lock for the duration of the batch lookup so that a\n\t// concurrent Close() (which takes the write lock and sets m.m to nil)\n\t// cannot yank the underlying map out from under us mid-iteration.\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\tif m.m == nil {\n\t\treturn 0, fmt.Errorf(\"%s: %w\", m.name, ErrMapNotOpened)\n\t}\n\n\treturn m.m.BatchLookup(cursor, keysOut, valuesOut, opts)\n}\n\n// DumpIfExists dumps the contents of the map into hash via Dump() if the map\n// file exists\nfunc (m *Map) DumpIfExists(hash map[string][]string) error {\n\tfound, err := m.exist()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif found {\n\t\treturn m.Dump(hash)\n\t}\n\n\treturn nil","sourceCodeStart":1185,"sourceCodeEnd":1221,"githubUrl":"https://github.com/cilium/cilium/blob/ac7b90affa4baf0642e6685319d56907b3a73a6d/pkg/bpf/map_linux.go#L1185-L1221","documentation":"BatchLookup is a method on the Map's iterator access path that requires the underlying ebpf map to be open. The read lock is taken to guard against concurrent Close(), and if m.m is nil — the map was never opened or was closed concurrently — the sentinel error ErrMapNotOpened wrapped with the map name is returned.","triggerScenarios":"Calling BatchLookup before Open()/OpenOrCreate() on the Map, or after another goroutine called Close(), or after a failed open left m.m nil.","commonSituations":"Skipping the Open step in a fast-path code path that assumed the map was always open; shutdown ordering where Close runs while iteration is still in flight; open failure earlier in startup ignored and later calls hit the nil map.","solutions":["Call Open() (or OpenOrCreate()) successfully before any BatchLookup use","Use errors.Is(err, bpf.ErrMapNotOpened) to detect this case and open the map lazily, then retry","Serialize Close against iteration via context cancellation or the library's locking conventions","Check startup logs for earlier open failures so the map is not silently left unopened"],"exampleFix":"// before\nn, err := m.BatchLookup(cursor, keys, vals, nil) // mymap: map not opened\n// after\nif err := m.Open(); err != nil { return err }\nn, err := m.BatchLookup(cursor, keys, vals, nil)\nif errors.Is(err, bpf.ErrMapNotOpened) { _ = m.Open(); /* retry */ }","handlingStrategy":"type-guard","validationCode":"func ensureOpen(m *bpf.Map) error {\n    if !m.IsOpen() { return m.Open() } // or equivalent check before batch lookups\n    return nil\n}","typeGuard":"func isOpen(m *bpf.Map) bool { return m != nil && m.IsOpen() }","tryCatchPattern":"n, err := m.BatchLookup(cursor, keys, vals, nil)\nif errors.Is(err, bpf.ErrMapNotOpened) {\n    if oerr := m.Open(); oerr != nil { return oerr }\n    n, err = m.BatchLookup(cursor, keys, vals, nil) // retry once\n}\nif err != nil { return err }","preventionTips":["Always Open()/OpenOrCreate() before BatchLookup","Cancel iteration via context before Close during shutdown","Treat ErrMapNotOpened as recoverable: open and retry once","Surface early open failures so maps are never silently unopened"],"tags":["ebpf","lifecycle","sentinel-error","race"],"backgroundTag":"bpf-map-not-opened","analyzedSha":"ac7b90affa4baf0642e6685319d56907b3a73a6d","analyzedAt":"2026-08-31T18:27:15.868Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}