Add WHERE tenant_id = 42 to a working vector query and one of two things happens: it gets slow, or it returns fewer rows than you asked for. Both are the same underlying fact — an approximate index and a filter cannot be applied at the same time — and the selectivity at which it becomes fatal is calculable in advance.

Why the two do not compose

A B-tree index and a GIN index can be combined: Postgres builds a bitmap from each and ANDs them. An HNSW index cannot participate in that, because it does not produce a set of matching rows — it produces an ordered stream, nearest first, and the ordering is the entire value of it. You cannot intersect an ordering with a bitmap and still have an ordering.

So the planner must choose. Either it walks the graph and discards rows that fail the filter afterwards (post-filter), or it finds the rows that pass the filter and computes distances for all of them (pre-filter). These have different costs and, critically, different answers.

Postgres chooses between them with its cost model, and its cost model for an approximate nearest-neighbour scan is close to a guess. It has no statistics describing how many rows the graph will examine before finding ten that pass your filter, because that quantity depends on where in vector space your query landed. So the planner picks using an estimate that is structurally uninformed, and it will sometimes pick the plan that cannot answer your query. This is the one situation in Postgres where overriding the planner from the application is normal rather than a smell.