krisk/Fuse · error · Error
Invalid doc index: must be a non-negative integer within the
Error message
Invalid doc index: must be a non-negative integer within the bounds of the docs array
What it means
Fuse.removeAt(idx) validates its argument before any mutation: idx must be an integer, non-negative, and within bounds of the docs array. The check is atomic — an invalid call leaves the collection and inverted index completely untouched (the old implementation spliced first and threw after, leaving partial state).
Source
Thrown at src/core/index.ts:198
}
// Filter docs in a single pass instead of reverse-splicing
const toRemove = new Set(indicesToRemove)
this._docs = this._docs.filter((_, i) => !toRemove.has(i))
this._myIndex.removeAll(indicesToRemove)
this._invalidateSearcherCache()
}
return results
}
removeAt(idx: number): T {
// Validate before any mutation. The previous code spliced `_docs` first
// and let FuseIndex.removeAt throw afterward — partial-state on invalid
// input. Atomic now.
if (!Number.isInteger(idx) || idx < 0 || idx >= this._docs.length) {
throw new Error(ErrorMsg.INVALID_DOC_INDEX)
}
if (this._invertedIndex) {
removeAndShiftInvertedIndex(this._invertedIndex, [idx])
}
const doc = this._docs.splice(idx, 1)[0]
this._myIndex.removeAt(idx)
this._invalidateSearcherCache()
return doc
}
_invalidateSearcherCache(): void {
this._lastQuery = null
this._lastSearcher = null
}
getIndex(): FuseIndex<T> {
return this._myIndexView on GitHub (pinned to edf2fb608e)
Solutions
- Validate before calling: ensure Number.isInteger(idx) && idx >= 0 && idx < docs.length.
- Prefer remove(doc) or remove(predicate) when you have the document object instead of positional removal.
- Recompute the index immediately before removal rather than caching it; iterate backwards when removing multiple items.
Example fix
// before list.forEach((_, i) => fuse.removeAt(i)) // out of bounds as list shrinks // after for (let i = list.length - 1; i >= 0; i--) fuse.removeAt(i)
Defensive patterns
Strategy: validation
Validate before calling
const canRemoveAt = (docsLen, idx) =>
Number.isInteger(idx) && idx >= 0 && idx < docsLen
if (canRemoveAt(docs.length, idx)) {
fuse.removeAt(idx)
} Type guard
const isValidDocIndex = (idx, len) => Number.isInteger(idx) && idx >= 0 && idx < len
Try / catch
try {
fuse.removeAt(idx)
} catch (e) {
if (String(e.message).startsWith('Invalid doc index')) {
return // stale index / already removed; collection untouched
}
throw e
} Prevention
- Recompute indices immediately before removal; never cache them across mutations.
- Iterate backwards when removing multiple items by index.
- Prefer remove(predicate) over positional removal when you hold the document.
When it happens
Trigger: fuse.removeAt(-1), a float like 1.5, NaN/undefined from an unbound handler, or an index >= docs.length — e.g. reusing a stale index captured before prior removals shrank the collection.
Common situations: Removing items in a loop without adjusting indices; deleting based on a cached position; passing an id or event object instead of the numeric position.
Related errors
AI-assisted analysis of krisk/Fuse@edf2fb608e (2026-09-02).
Data as JSON: /api/errors/84bc986dcc433010.
Report an issue: GitHub.