Summary
operandToRank at pkg/api/v2/rank.go:1230-1233 silently substitutes Val(0) when called with a nil Operand:
func operandToRank(operand Operand) Rank {
if operand == nil {
return Val(0)
}
...
}
This means that any programmer error like:
rrf.Add(nil)
rrf.Multiply(nil)
rrf.Sub(nil)
is silently turned into rrf + 0, rrf * 0, rrf - 0 — the query runs, the search returns results, and the only signal that something is wrong is the mathematically-transformed score.
Impact
- All 10
RrfRank.* arithmetic methods fixed in Phase 21 are now affected by this footgun (they were previously no-ops, so the nil case was harmless).
- Every other
*Rank type that calls operandToRank (KnnRank, ValRank, SumRank, SubRank, MulRank, DivRank, AbsRank, ExpRank, LogRank, MaxRank, MinRank) has the same exposure.
Proposed Fix
Prefer the UnknownRank pattern already used in the default branch (rank.go:1244) — return &UnknownRank{} for nil as well. UnknownRank.MarshalJSON at rank.go:85 already fails loud with a clear error message:
UnknownRank: cannot marshal unknown operand type - this indicates a programming error
This converts a silent runtime footgun into a loud serialization-time failure that the caller can act on.
Alternative
Add a nil-check at each method entry point and return a rank with a deferred error (the error-returning pattern already used elsewhere in the package).
Discovery
Flagged during PR review of #496 by the silent-failure-hunter agent.
Summary
operandToRankatpkg/api/v2/rank.go:1230-1233silently substitutesVal(0)when called with a nilOperand:This means that any programmer error like:
is silently turned into
rrf + 0,rrf * 0,rrf - 0— the query runs, the search returns results, and the only signal that something is wrong is the mathematically-transformed score.Impact
RrfRank.*arithmetic methods fixed in Phase 21 are now affected by this footgun (they were previously no-ops, so the nil case was harmless).*Ranktype that callsoperandToRank(KnnRank, ValRank, SumRank, SubRank, MulRank, DivRank, AbsRank, ExpRank, LogRank, MaxRank, MinRank) has the same exposure.Proposed Fix
Prefer the
UnknownRankpattern already used in the default branch (rank.go:1244) — return&UnknownRank{}for nil as well.UnknownRank.MarshalJSONatrank.go:85already fails loud with a clear error message:This converts a silent runtime footgun into a loud serialization-time failure that the caller can act on.
Alternative
Add a nil-check at each method entry point and return a rank with a deferred error (the error-returning pattern already used elsewhere in the package).
Discovery
Flagged during PR review of #496 by the silent-failure-hunter agent.