GitoxideLabs/gitoxide · error

non zero

Error message

non zero

What it means

`MemoryCappedHashmap::new` wraps its byte limit in a `NonZeroUsize` for clru's cache; a zero memory cap is a programming error because a cache that can hold nothing is meaningless. It panics with 'non zero' when `memory_cap_in_bytes == 0`.

Solutions

  1. Pass a non-zero memory cap (e.g. 64 * 1024 * 1024 for 64 MiB)
  2. Validate/clamp the config value before constructing: `if cap == 0 { cap = DEFAULT_CAP }`
  3. Use a cache-free code path if caching is unwanted rather than a 0 cap

Example fix

// before
let cache = MemoryCappedHashmap::new(config.memory_cap); // panics if 0
// after
let cap = config.memory_cap.max(1);
let cache = MemoryCappedHashmap::new(cap);
Defensive patterns

Strategy: validation

Validate before calling

assert!(memory_cap_in_bytes > 0, "memory cap must be non-zero");
// or: let cap = memory_cap_in_bytes.max(1);

Prevention

When it happens

Trigger: Constructing `gix_pack::cache::lru::MemoryCappedHashmap::new(0)`, usually from a configuration value of 0 for the object cache memory cap.

Common situations: Configuration mistakes: users setting cache memory to 0 intending 'unlimited' (it actually means 'no capacity at all'); config parsed as 0 by default.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/4d4fb8f9e8b50ad7. Report an issue: GitHub.

Appendix: source

Thrown at gix-pack/src/cache/lru.rs:40

        fn weight(&self, _key: &Key, value: &Entry) -> usize {
            value.data.len()
        }
    }

    /// An LRU cache with hash map backing and an eviction rule based on the memory usage for object data in bytes.
    pub struct MemoryCappedHashmap {
        inner: clru::CLruCache<Key, Entry, std::collections::hash_map::RandomState, CustomScale>,
        free_list: Vec<Vec<u8>>,
        debug: gix_features::cache::Debug,
    }

    impl MemoryCappedHashmap {
        /// Return a new instance which evicts least recently used items if it uses more than `memory_cap_in_bytes`
        /// object data.
        pub fn new(memory_cap_in_bytes: usize) -> MemoryCappedHashmap {
            MemoryCappedHashmap {
                inner: clru::CLruCache::with_config(
                    clru::CLruCacheConfig::new(NonZeroUsize::new(memory_cap_in_bytes).expect("non zero"))
                        .with_scale(CustomScale),
                ),
                free_list: Vec::new(),
                debug: gix_features::cache::Debug::new(format!("MemoryCappedHashmap({memory_cap_in_bytes}B)")),
            }
        }
    }

    impl DecodeEntry for MemoryCappedHashmap {
        fn put(&mut self, pack_id: u32, offset: u64, data: &[u8], kind: gix_object::Kind, compressed_size: usize) {
            self.debug.put();
            let Some(data) = set_vec_to_slice(self.free_list.pop().unwrap_or_default(), data) else {
                return;
            };
            let res = self.inner.put_with_weight(
                (pack_id, offset),
                Entry {
                    data,

View on GitHub (pinned to e73179060b)