Vector Search in .NET: Common Mistakes with Embedding Dimensions, HNSW Index Limits, Score Fusion & Re-Embedding

Verified against .NET 10, Microsoft.Extensions.AI 10.x, and current pgvector guidance. Last reviewed: July 23, 2026. Vector APIs change quickly, but the dimension, indexing, fusion, and re-embedding rules below are fundamental.

The Query Runs—But the Ranking Is Still Wrong

Vector search failures are not always compile errors. An application can generate embeddings, store them, create an index, and return ten results while still being structurally wrong. A dimension mismatch may appear only during ingestion. An HNSW migration may fail after a model upgrade. A hybrid ranker may silently favor one retriever because raw scores were added together.

These mistakes share one cause: treating an embedding like an ordinary numeric array. An embedding belongs to a specific model, dimension, distance metric, and indexing strategy. The retrieval pipeline must preserve those assumptions from ingestion through search and later upgrades.

Mistake 1: Letting the Model and Database Disagree on Dimensions

Every embedding model returns a vector with a fixed or configured number of elements. The PostgreSQL column must use the same dimension. A vector(1536) column cannot accept a 768-element local embedding or a 3,072-element cloud embedding.

The safest design stores the embedding model and expected dimension in configuration, validates every generated vector before saving it, and treats a dimension change as a schema and data migration. Matching dimensions are necessary, but they do not make embeddings from different models compatible.

EmbeddingDimensionGuard.cs
public sealed record SearchOptions(string EmbeddingModel, int EmbeddingDimensions);

static void ValidateEmbedding(ReadOnlyMemory<float> vector, SearchOptions options)
{
    if (vector.Length != options.EmbeddingDimensions)
    {
        throw new InvalidOperationException(
            $"Model '{options.EmbeddingModel}' returned {vector.Length} dimensions, " +
            $"but the database expects {options.EmbeddingDimensions}.");
    }
}

ReadOnlyMemory<float> vector =
    await embeddingGenerator.GenerateVectorAsync(text, cancellationToken: ct);

ValidateEmbedding(vector, options);

Mistake 2: Choosing a Model the HNSW Index Cannot Store

High-dimensional embeddings create a second problem: the column may store them, but the index may not support them. pgvector regular vector indexing supports up to 2,000 dimensions. A 3,072-dimension embedding therefore does not fit the normal HNSW plan.

The usual fixes are to request a reduced dimension when the provider supports it, select a smaller embedding model, use halfvec indexing for up to 4,000 dimensions, or choose another strategy. Confirm the model output and index limit before committing to a schema.

PgvectorIndex.sql
CREATE TABLE "DocumentChunks"
(
    "Id" uuid PRIMARY KEY,
    "Text" text NOT NULL,
    "Embedding" vector(1536) NOT NULL
);

CREATE INDEX "IX_DocumentChunks_Embedding_Hnsw"
ON "DocumentChunks"
USING hnsw ("Embedding" vector_cosine_ops);

-- A 3,072-dimension value cannot use this regular vector HNSW index.
-- Reduce dimensions, use a smaller model, evaluate halfvec,
-- or select another store/index strategy.

Mistake 3: Adding Keyword Rank and Vector Similarity Together

Hybrid search produces two ranked lists: full-text search and vector similarity. Their raw scores do not share a common scale or meaning. Adding them directly gives the numerically larger scoring system more influence.

Reciprocal Rank Fusion is a safer default because it combines rank positions rather than raw scores. A document receives 1 / (k + rank) from each list in which it appears. Documents ranked highly by both retrievers rise naturally, and a common starting value is k = 60.

ReciprocalRankFusion.cs
public static IReadOnlyList<FusedResult> Fuse(
    IReadOnlyList<Guid> keywordIds,
    IReadOnlyList<Guid> vectorIds,
    int rrfK = 60)
{
    var scores = new Dictionary<Guid, double>();
    AddRanks(keywordIds, scores, rrfK);
    AddRanks(vectorIds, scores, rrfK);

    return scores
        .OrderByDescending(x => x.Value)
        .Select(x => new FusedResult(x.Key, x.Value))
        .ToArray();
}

static void AddRanks(IReadOnlyList<Guid> ids, IDictionary<Guid,double> scores, int rrfK)
{
    for (int i = 0; i < ids.Count; i++)
        scores[ids[i]] = scores.GetValueOrDefault(ids[i]) + 1.0 / (rrfK + i + 1);
}

Mistake 4: Changing the Embedding Model Without Re-Embedding the Corpus

Stored vectors are not generic semantic coordinates. They belong to the model that created them. If queries use a new model while documents still contain old vectors, distance comparisons lose meaning even when both models return the same dimension.

Treat a model upgrade as a data migration. Build a new index version, regenerate the complete corpus in batches, validate retrieval quality, and switch traffic only after the replacement is ready. Store model id, dimension, and index version so mixed data is detectable.

VersionedEmbedding.cs
public sealed class DocumentChunk
{
    public Guid Id { get; set; }
    public required string Text { get; set; }
    public required Vector Embedding { get; set; }
    public required string EmbeddingModel { get; set; }
    public int EmbeddingDimensions { get; set; }
    public int EmbeddingIndexVersion { get; set; }
}

var active = db.DocumentChunks.Where(x =>
    x.EmbeddingModel == options.EmbeddingModel &&
    x.EmbeddingIndexVersion == options.ActiveIndexVersion);

How to Check a Vector Index Before You Trust It

Start with metadata: embedding model, output dimension, column dimension, distance function, and active index version. Then run a small labeled query set in keyword-only, vector-only, and hybrid modes.

A fast HNSW index can still be a poor search system if chunks, dimensions, candidate depth, or fusion logic are wrong. Measure retrieval quality separately from latency.

VectorSearchReadinessChecklist.txt
[ ] Embedding model id is stored and versioned
[ ] Generated vector length matches configuration
[ ] Database vector(N) uses the same dimension
[ ] HNSW supports the selected type and dimension
[ ] Distance operator matches the index operator class
[ ] Keyword and vector raw scores are not added directly
[ ] RRF fuses ranked candidate lists
[ ] Candidate depth exceeds the final top count
[ ] Model changes trigger full re-embedding
[ ] Approximate search is compared with exact search
[ ] Retrieval quality is tested with labeled queries

What Developers Want to Know

Why does pgvector reject my embedding or migration?

The generated vector does not match the dimension declared by the database column, or the selected index cannot support that dimension. Validate vector length before saving and keep the model, dimension, and column definition aligned.

Can pgvector create an HNSW index on a 3072-dimension vector?

Not with the regular vector type. pgvector indexes vector values up to 2,000 dimensions. Request a smaller embedding when supported, use halfvec indexing up to 4,000 dimensions, or choose another indexing strategy.

Why should I not add vector similarity and full-text rank together?

The scores use different scales and meanings. Adding them lets one retriever dominate unpredictably. Reciprocal Rank Fusion combines rank positions instead of raw scores.

Do I need to re-embed documents when I change the embedding model?

Yes. Embeddings from different models or versions do not share a reliable vector space. Rebuild the corpus into a new index, validate retrieval, and switch only after the new index is ready.

Back to Articles