RyanCodrai/turbovec · error · io::Error
{} ids for {} rows
Error message
{} ids for {} rows What it means
IdMap::from_index_and_ids requires the external-id vector to have exactly one id per row of the inner TurboQuantIndex. A length mismatch means the id metadata and the index rows disagree, so an InvalidData io::Error with the counts is returned instead of building a misaligned map.
Source
Thrown at turbovec/src/id_map.rs:904
let l = crate::io_v7::load(path, 0, 1)?;
// See TurboQuantIndex::load_v7 — an unclaimed snapshot loads
// unbound so the first sync claims it.
let bind = (l.cursor.nonce != crate::io_v7::UNCLAIMED_NONCE).then_some(path);
Self::from_v7_load(l, bind)
}
/// Wrap an already-built index with an id table.
///
/// Used by [`crate::convert`], which decodes a file into codes plus
/// ids and needs to re-emit it: the ids are validated for duplicates
/// exactly as a load does, since a table with a repeat cannot answer
/// `remove` or `contains` unambiguously.
pub(crate) fn from_index_and_ids(
inner: TurboQuantIndex,
slot_to_id: Vec<u64>,
) -> std::io::Result<Self> {
if slot_to_id.len() != inner.len() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("{} ids for {} rows", slot_to_id.len(), inner.len()),
));
}
let mut sorted = slot_to_id.clone();
sorted.sort_unstable();
if sorted.windows(2).any(|w| w[0] == w[1]) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"duplicate ids",
));
}
Ok(Self {
inner,
slot_to_id,
id_to_slot: std::sync::OnceLock::new(),
sorted_ids: std::sync::Mutex::new(sorted),
deferred_added: std::sync::Mutex::new(Default::default()),View on GitHub (pinned to ccab9f325e)
Solutions
- Regenerate the id vector so it has exactly one entry per index row in the same order.
- Ensure the index and ids come from the same build/checkpoint — rebuild both together.
- If ids were filtered, apply the same filtering to the index (or vice versa) before calling from_index_and_ids.
Example fix
// before let map = IdMap::from_index_and_ids(index, stale_ids)?; // 1000 ids for 900 rows // after assert_eq!(stale_ids.len(), index.len(), "ids/index length mismatch"); let map = IdMap::from_index_and_ids(index, stale_ids)?;
Defensive patterns
Strategy: validation
Validate before calling
if ids.len() != index.len() {
return Err(format!("{} ids for {} rows", ids.len(), index.len()));
} Try / catch
let map = IdMap::from_index_and_ids(index, ids)
.map_err(|e| { log::error!("id map build failed: {e}"); BuildError::IdMismatch })?; Prevention
- Always build ids and index in the same pass; never reuse a stale id vector.
- Keep index+ids artifacts versioned together (same checkpoint).
- Assert length equality after every remove/filter operation.
When it happens
Trigger: Constructing an IdMap (or wrapping API) with slot_to_id.len() != inner.len() — e.g. ids truncated, duplicated rows filtered from the index without updating ids, or loading an ids file from a different index build.
Common situations: Rebuilding an index with remove/rebuild and reusing a stale id vector; loading mismatched artifacts (index from one checkpoint, ids from another); off-by-one in a serialization step that dropped ids.
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
- duplicate ids
- duplicate ids in v7 file
- persisted store is corrupt: duplicate {what} handles in the
- persisted store is corrupt: duplicate node handles in the si
- {e}
AI-assisted analysis of RyanCodrai/turbovec@ccab9f325e (2026-09-06).
Data as JSON: /api/errors/e84aac4e50b5ae9d.
Report an issue: GitHub.