chroma-core/chroma · error · TypeError
${context} must be a rank expression
Error message
${context} must be a rank expression What it means
Thrown by requireRank (rank.ts:437) when a value in a rank-operand position fails to convert via RankExpression.from. Because from() accepts RankExpression instances, plain numbers, and plain objects — and throws its own distinct TypeError for other types — this message fires specifically when the operand is null or undefined. The ${context} prefix names the exact slot: 'Sub left', 'Div right', 'Sum operand 0', 'ranks[1]', 'add operand 0', 'Abs', 'Exp', 'Log', etc.
Source
Thrown at clients/new-js/packages/chromadb/src/execution/expression/rank.ts:437
throw new TypeError("Knn default must be a finite number");
}
return {
query:
Array.isArray(query) || typeof query === "string"
? query
: deepClone(query),
key,
limit,
defaultValue,
returnRank: options.returnRank ?? false,
};
};
const requireRank = (input: RankInput, context: string): RankExpression => {
const result = RankExpression.from(input);
if (!result) {
throw new TypeError(`${context} must be a rank expression`);
}
return result;
};
export const Val = (value: number): RankExpression =>
new ValueRankExpression(requireNumber(value, "Val requires a numeric value"));
export const Knn = (options: KnnOptions): RankExpression =>
new KnnRankExpression(normalizeKnnOptions(options));
export interface RrfOptions {
ranks: RankInput[];
k?: number;
weights?: number[];
normalize?: boolean;
}
export const Rrf = ({View on GitHub (pinned to aecdd12c8a)
Solutions
- Find the slot named in the message (e.g. 'Sub left' = first argument of Sub) and ensure it is a RankExpression, number, or plain-object literal
- Filter null/undefined out of operand arrays before spreading: parts.filter(p => p !== null && p !== undefined)
- Give optional components an explicit numeric default: maybeRank ?? 0
Example fix
// before const rank = Sum(...components); // components may contain undefined // after const rank = Sum( ...components.filter((p): p is Exclude<typeof p, null | undefined> => p != null), );
Defensive patterns
Strategy: validation
Validate before calling
const compact = (parts: RankInput[]): RankInput[] =>
parts.filter((p): p is Exclude<RankInput, null | undefined> => p != null);
if (compact(parts).length === 0) throw new Error('no rank components');
const rank = Sum(...compact(parts)); Type guard
const isNonNullRankInput = (v: unknown): v is RankInput => v instanceof RankExpression || typeof v === 'number' || typeof v === 'object';
Try / catch
try {
const rank = Sub(left, right);
} catch (e) {
if (e instanceof TypeError && e.message.includes('must be a rank expression')) {
// message names the slot, e.g. 'Sub left' — fix that exact operand
throw new Error(`bad rank operand: ${e.message}`);
}
throw e;
} Prevention
- Filter null/undefined from operand arrays before spreading
- Do not use .filter(Boolean) blindly — it also drops 0, which is a valid numeric rank literal
- Give optional components a default: maybeRank ?? 0
When it happens
Trigger: Sub(null, Val(1)); expr.add(undefined); Mul(Val(2), null); Rrf({ ranks: [Val(1), null] }); Abs(undefined). Most common real shape: building operands from an array containing optional/missing entries — Sum(...parts) where parts includes null or undefined.
Common situations: Conditionally assembled rank expressions where one branch produced undefined (e.g. an if without else); map lookups (dictionary.get(key)) returning undefined; API responses where a scoring component is absent; TypeScript any-typed pipelines that defeat compile-time checks.
Related errors
- Knn limit must be a positive integer
- Rrf k must be a positive integer
- Sum requires at least one rank expression
- Mul requires at least one rank expression
- Max requires at least one rank expression
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/dbdc4b5bf0b10428.
Report an issue: GitHub.