cilium/cilium · error

ErrMapNotOpened

ErrMapNotOpened

Error message

%s: %w

What it means

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.

Source

Thrown at pkg/bpf/map_linux.go:1203

	if err := m.DumpWithCallback(callback); err != nil {
		return err
	}

	return nil
}

// BatchLookup returns the count of elements in the map by dumping the map
// using batch lookup.
func (m *Map) BatchLookup(cursor *ebpf.MapBatchCursor, keysOut, valuesOut any, opts *ebpf.BatchOptions) (int, error) {
	// Hold the read lock for the duration of the batch lookup so that a
	// concurrent Close() (which takes the write lock and sets m.m to nil)
	// cannot yank the underlying map out from under us mid-iteration.
	m.lock.RLock()
	defer m.lock.RUnlock()

	if m.m == nil {
		return 0, fmt.Errorf("%s: %w", m.name, ErrMapNotOpened)
	}

	return m.m.BatchLookup(cursor, keysOut, valuesOut, opts)
}

// DumpIfExists dumps the contents of the map into hash via Dump() if the map
// file exists
func (m *Map) DumpIfExists(hash map[string][]string) error {
	found, err := m.exist()
	if err != nil {
		return err
	}

	if found {
		return m.Dump(hash)
	}

	return nil

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Call Open() (or OpenOrCreate()) successfully before any BatchLookup use
  2. Use errors.Is(err, bpf.ErrMapNotOpened) to detect this case and open the map lazily, then retry
  3. Serialize Close against iteration via context cancellation or the library's locking conventions
  4. Check startup logs for earlier open failures so the map is not silently left unopened

Example fix

// before
n, err := m.BatchLookup(cursor, keys, vals, nil) // mymap: map not opened
// after
if err := m.Open(); err != nil { return err }
n, err := m.BatchLookup(cursor, keys, vals, nil)
if errors.Is(err, bpf.ErrMapNotOpened) { _ = m.Open(); /* retry */ }
Defensive patterns

Strategy: type-guard

Validate before calling

func ensureOpen(m *bpf.Map) error {
    if !m.IsOpen() { return m.Open() } // or equivalent check before batch lookups
    return nil
}

Type guard

func isOpen(m *bpf.Map) bool { return m != nil && m.IsOpen() }

Try / catch

n, err := m.BatchLookup(cursor, keys, vals, nil)
if errors.Is(err, bpf.ErrMapNotOpened) {
    if oerr := m.Open(); oerr != nil { return oerr }
    n, err = m.BatchLookup(cursor, keys, vals, nil) // retry once
}
if err != nil { return err }

Prevention

When it happens

Trigger: Calling BatchLookup before Open()/OpenOrCreate() on the Map, or after another goroutine called Close(), or after a failed open left m.m nil.

Common situations: 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.

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/5d7367ecbaa02f86. Report an issue: GitHub.