#700·annoy

[Bug] Passing a negative number for 'n' to 'get_nns_by_item' or 'get_nns_by_vector' causes incorrect results and integer overflow

Author: saitejabandaru-inCreated Jul 20, 2026Updated Jul 20, 2026

When querying an Annoy index using the Python API, if a negative integer (e.g. n = -1) is passed as the n argument to get_nns_by_item or get_nns_by_vector, the function returns completely incorrect results or empty lists [] rather than throwing a Python exception.

Reproduction

python
from annoy import AnnoyIndex

t = AnnoyIndex(10, 'angular')
for i in range(10):
    v = [1 if j == i else 0 for j in range(10)]
    t.add_item(i, v)
t.build(10)

# Expected: [0], but gets completely unrelated item or empty list
print(t.get_nns_by_item(0, -1))
print(t.get_nns_by_item(0, -2))

Root Cause

In annoymodule.cc, n is parsed as a signed int32_t:

cpp
int32_t item, n, search_k=-1, include_distances=0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii", (char**)kwlist, &item, &n, &search_k, &include_distances))

However, in annoylib.h, the corresponding argument is an unsigned size_t n:

cpp
void get_nns_by_item(S item, size_t n, int search_k, vector<S>* result, vector<T>* distances) const

When n = -1 is passed, it is sign-extended to SIZE_MAX (e.g., 18446744073709551615).

This leads to cascading issues in _get_all_nns:

  1. search_k Overflow: search_k = n * _roots.size(); overflows. Because search_k is int, it silently wraps around to a negative number.
  2. Unbounded Search Condition: while (nns.size() < (size_t)search_k && !q.empty()) converts the negative search_k back to SIZE_MAX, causing the search to attempt exploring the entire tree unbounded.
  3. Garbage Results: size_t p = n < m ? n : m; evaluates to m (because SIZE_MAX < m is false), causing all explored items to be returned. Due to unbounded exploration boundaries, this often yields completely random incorrect items or [].

Suggested Fix

Add a parameter constraint check in annoymodule.cc to ensure n >= 0 before making calls to the C++ core:

cpp
if (n < 0) {
    PyErr_SetString(PyExc_ValueError, "n must be a non-negative integer");
    return NULL;
}