chroma-core/chroma · error · TypeError
Rrf requires at least one rank expression
Error message
Rrf requires at least one rank expression
What it means
Thrown by Rrf (rank.ts:465) when its `ranks` option is not a non-empty array. RRF fuses two or more ranked lists, so at least one rank expression is structurally required. The check is Array.isArray(ranks) && ranks.length > 0; a single bare expression object or an empty array both throw. Remember k is validated first, so an invalid k will surface before this error.
Source
Thrown at clients/new-js/packages/chromadb/src/execution/expression/rank.ts:465
export interface RrfOptions {
ranks: RankInput[];
k?: number;
weights?: number[];
normalize?: boolean;
}
export const Rrf = ({
ranks,
k = 60,
weights,
normalize = false,
}: RrfOptions): RankExpression => {
if (!Number.isInteger(k) || k <= 0) {
throw new TypeError("Rrf k must be a positive integer");
}
if (!Array.isArray(ranks) || ranks.length === 0) {
throw new TypeError("Rrf requires at least one rank expression");
}
const expressions = ranks.map((rank, index) =>
requireRank(rank, `ranks[${index}]`),
);
let weightValues = weights
? weights.slice()
: new Array(expressions.length).fill(1);
if (weightValues.length !== expressions.length) {
throw new Error("Number of weights must match number of ranks");
}
if (weightValues.some((value) => typeof value !== "number" || value < 0)) {
throw new TypeError("Weights must be non-negative numbers");
}
if (normalize) {
const total = weightValues.reduce((sum, value) => sum + value, 0);View on GitHub (pinned to aecdd12c8a)
Solutions
- Ensure ranks is an array with at least one element: Rrf({ ranks: [textRank, vectorRank] })
- For a single rank, skip RRF and use the expression directly, or still wrap it: Rrf({ ranks: [onlyRank] })
- Guard dynamic lists before fusing: if (ranks.length === 0) return fallbackRank;
Example fix
// before
const fused = Rrf({ ranks: retrievers.filter(enabled) }); // may be []
// after
const active = retrievers.filter(enabled);
const fused = active.length > 1
? Rrf({ ranks: active })
: active[0]; // single or zero retriever: no fusion needed Defensive patterns
Strategy: validation
Validate before calling
if (!Array.isArray(ranks) || ranks.length === 0) {
throw new Error('RRF requires at least one retriever');
}
const fused = Rrf({ ranks }); Type guard
const isNonEmptyRankArray = (v: unknown): v is RankInput[] => Array.isArray(v) && v.length > 0;
Try / catch
try {
const fused = Rrf({ ranks });
} catch (e) {
if (e instanceof TypeError && e.message.includes('at least one rank')) {
return singleRank; // degrade to the single retriever
}
throw e;
} Prevention
- Wrap single expressions in an array: Rrf({ ranks: [only] })
- Gate fusion on list length: fuse only when activeRetrievers.length > 1
- Validate k first if both are dynamic — k errors mask this one
When it happens
Trigger: Calling Rrf({ ranks: [] }) — e.g. the list of candidate retrievers was empty at runtime; Rrf({ ranks: Val(1) }) — passing one expression directly instead of wrapping it in an array; Rrf({ ranks: denseRank }) where a variable holding a single expression was not arrayed.
Common situations: Hybrid search pipelines that fuse 'all available retrievers'; user deselects every retriever in a UI so the fusion list becomes empty; conditional branches that push retrievers only when features are enabled; refactor from multiple named params to a ranks array where one call site was missed.
Related errors
- Rrf k must be a positive integer
- Number of weights must match number of ranks
- Weights must be non-negative numbers
- $and must be a non-empty array
- Knn limit must be a positive integer
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/a753c3cfd5e3c674.
Report an issue: GitHub.