This guide covers common issues with Virtuoso vector indexes, from build problems to query errors. When something goes wrong, start with the health check sequence below to narrow down the fault domain, then find your specific symptom in the category that matches.
This reference covers:
- Overview: How to approach vector index troubleshooting
- Health check: A quick diagnostic sequence to run before deep troubleshooting
- Plugin and model issues: Missing ONNX BIFs, model load failures, bootstrap errors
- Index creation issues: Duplicate names, dimension mismatches, storage limits
- Query issues: Empty results, NULL distances, stale data, full scans, text parameters, access denied
- Build performance issues: Long refreshes, slow updates to bulk-built indexes, remote-provider builds
- HTTP provider issues: Authentication, rate limits, latency
- SPARQL-specific issues: Missing index options, graph ACLs, selector options
- Upgrade issues: Stored copies of built-in procedures
- Durability and rebuild issues: Indexes that refuse searches, rebuild errors
- Performance issues: Slow builds, high latency, high memory usage
- Diagnostic functions quick reference: A table of functions for inspecting index health
Overview
Vector index troubleshooting usually falls into one of a few fault domains: the ONNX plugin and embedding model, the index creation itself, the query plan, the HTTP embedding provider, SPARQL-specific plumbing, or raw performance. Each symptom in this guide is grouped under the category it most commonly belongs to.
Health check
A quick diagnostic sequence to run before deep troubleshooting. Work through the steps in order — each one narrows down where the fault lies.
-- 1. Verify the vonnx plugin loaded (check server log for
-- "SUCCESS plugin N: loaded from .../vonnx.so")
-- 2. Verify the model is loaded:
SELECT model_name, model_type, handle FROM DB.DBA.ONNX_MODELS;
-- For local models (text_embedding), handle must be non-null
-- For HTTP providers (openai, ollama, etc.), handle is null (expected)
-- 3. Test embedding:
SELECT DB.DBA.ONNX_EMBED_TEXT_FOR_MODEL('granite-384-local', 'health check');
-- Expect: a 384-element float vector
-- 4. Verify index exists and is populated:
SELECT obj2json (__VEC_INDEX_METRICS ('DOCS_VEC'));
-- managed_rows and graph_nodes should be > 0
-- state should be 'READY_FOR_INTERNAL_LOAD'
-- (for a SPARQL index, also check SVI_STATE = 'active' in DB.DBA.SPARQL_VEC_INDEX)
-- 5. Verify search works:
SELECT __VEC_INDEX_SEARCH('DOCS_VEC', DB.DBA.ONNX_EMBED_TEXT_FOR_MODEL('granite-384-local', 'test'), 10, 100);
-- Expect: a vector of (row id, distance) pairs, nearest first (not empty)Plugin and model issues
Symptom: SR185: Undefined procedure DB.DBA.ONNX_EMBED_TEXT, or "Model granite-384-local is not present in DB.DBA.ONNX_MODELS"
- Cause: The
vonnxplugin is not loaded, so the ONNX BIFs are unregistered.ONNX_EMBED_TEXTthen fails withSR185; on a new database the bundled model is never registered, soONNX_EMBED_TEXT_FOR_MODELreports thatgranite-384-localis not present inDB.DBA.ONNX_MODELSandCREATE VECTOR INDEXfails withVEC18. The server log has noSUCCESS plugin N: loaded from .../vonnx.soline, and has instead:Output ONNX bootstrap disabled granite-384-local: helper configuration failed for …/onnx_helper: [42001] SR185: Undefined - Fix: Load
vonnxin the[Plugins]section ofvirtuoso.ini, and say where the model lives in[ONNX]. The installedvirtuoso.inialready does, when the server was built with the ONNX plugin; with the paths of a defaultmake install:
[Plugins]
LoadPath = /usr/local/virtuoso-opensource/lib/virtuoso/hosting
Load17 = plain, vonnx
[ONNX]
ModelRoot = /usr/local/virtuoso-opensource/lib/virtuoso/hosting/onnx-modelsUse your installation's hosting directory; the LoadN number only has to be unique within [Plugins]. Restart the server and confirm the log shows "SUCCESS plugin N: loaded from .../vonnx.so". If the plugin was not built at all, configure the server with --enable-onnxruntime=<dir> and build it again.
Symptom: handle is NULL for a local ONNX model
- Cause: The model files or the helper couldn't be found or loaded — usually a path issue
- Fix: - If the server log has a line beginning
ONNX bootstrap disabled granite-384-local: model=<unresolved> vocab=<unresolved> …, the bootstrap could not find the bundled model's files. Set[ONNX] ModelRootinvirtuoso.inito the installedonnx-modelsdirectory (or setGraniteModelPathandGraniteVocabPath), andHelperPathifonnx_helperis not in the pluginLoadPath; use absolute paths, and restart. Until then, a new database has nogranite-384-localrow at all; a database that had one keeps it withmodel_typeunavailable, a NULLhandle, and the full message indescription, and text searches on its indexes fail withVEC51: Automatic embedding for vector index … requires a loaded handle for model granite-384-local in DB.DBA.ONNX_MODELS.- Check the model path exists:SELECT model_path FROM DB.DBA.ONNX_MODELS WHERE model_name = 'granite-384-local'- Try refreshing:SELECT DB.DBA.ONNX_MODEL_REFRESH('granite-384-local')- Check the server log for ONNX load errors - ONNX Runtime is linked intovonnx.soandonnx_helper, so there is no separate runtime library to install; what must be found is an executableonnx_helper
Index creation issues
Symptom: CREATE VECTOR INDEX fails with "SQ022: Duplicate index name"
- Cause: An index of that name — vector or ordinary — already exists.
- Fix: Drop it with
DROP INDEX <name>;or choose another name.CREATE VECTOR INDEXandDROP INDEXare atomic: a create that fails leaves no index, hidden table or triggers behind, so there is nothing to clean up by hand before retrying.
Symptom: dimensions mismatch error
- Cause: The
WITH (dimensions = N)value doesn't match the model's output dimension:Output VEC19: Model granite-384-local output dimension 384 does not match requested vector dimension 256. - Fix: Use the correct dimension — the bundled
granite-384-local= 384, OpenAI text-embedding-3-small = 1536, etc. — or leavedimensionsout and let it come from the model. Check with:
SELECT output_shape FROM DB.DBA.ONNX_MODELS WHERE model_name = '...';The SPARQL DDL reports the same mismatch as VEC75; see SPARQL-specific issues.
Symptom: "VEC17: Vector storage cannot fit node metadata for hnsw_m=N"
- Cause:
hnsw_mis too large for a node page. Each node reserves room for 17 ×hnsw_mneighbour references on its 8 KB page, so abovehnsw_m= 118 not even a reference to an out-of-line vector fits (and above 95, a 384-dimension vector no longer fits inline and moves to overflow pages). - Fix: Use a smaller
hnsw_m. The default is 16, and 32 is the usual choice for high-dimensional models; see Index parameters.
Query issues
Symptom: Query returns 0 rows (empty result set)
- Cause: The HNSW graph is empty (no nodes)
- Diagnosis:
SELECT obj2json (__VEC_INDEX_METRICS ('DOCS_VEC'));
-- If managed_rows = 0 and graph_nodes = 0, the graph was never populated- Fix: If the rows were written after the index was created, drain the queue (see New rows don't appear). Otherwise drop and recreate the index:
DROP INDEX DOCS_VEC;
CREATE VECTOR INDEX DOCS_VEC ON DB.DBA.DOCS (content)
USING 'granite-384-local'
WITH (dimensions = 384, distance_metric = 'cosine');If the create succeeds but graph_nodes is still 0, check that the base table has rows and the model can embed.
Symptom: Query returns NULL for VECTOR_DISTANCE / X_SIMILAR
- Cause: The row has no embedding: it was written after the index was created and its change is still in the maintenance queue, or its text is
NULL, which is never embedded. - Fix: Drain the queue (below). A row whose text is
NULLkeeps aNULLdistance and score, and never appears in an index-driven search.
Symptom: New rows don't appear in search results
- Cause: The maintenance queue hasn't been drained. A scheduled worker drains it only every few minutes, and the SPARQL async queue is drained only when something calls a drain — see Index maintenance.
- Fix:
-- For SQL indexes:
SELECT DB.DBA.VEC_SQL_QUEUE_DRAIN(1000);
-- For SPARQL indexes with maintenance = 'async':
SELECT DB.DBA.VEC_INDEX_QUEUE_DRAIN(1000);
-- Or refresh the index (also picks up changes written straight into RDF_QUAD):
SPARQL DEFINE input:storage "" refresh VECTOR INDEX <urn:docs:idx> ;Symptom: EXPLAIN shows full table scan instead of ANN search
- Cause: The optimizer didn't recognize the
X_SIMILAR/VECTOR_DISTANCEcall as indexable - Fix: - Ensure
X_SIMILARorVECTOR_DISTANCEis in theWHEREclause orORDER BY, not just theSELECTlist - Ensure the column name in the function matches the indexed column - Check that a vector index exists on the table — each one has its<name>_VVEC_AImaintenance trigger:SELECT T_NAME FROM DB.DBA.SYS_TRIGGERS WHERE T_TABLE = 'DB.DBA.DOCS' AND T_NAME LIKE '%_VVEC_AI'. By name,SELECT __VEC_INDEX_METRICS ('DOCS_VEC')fails withVEC01when there is no such index andVEC02when it is not a vector index.
Symptom: VEC33 "Malformed query vector" for a text parameter
- Cause: The query text was bound as a parameter (
?, as ODBC, JDBC andexecpass it).X_SIMILARandVECTOR_DISTANCEembed text only when it is a literal in the statement; a parameter is taken as the query vector itself. - Fix: Embed the parameter and pass the vector — see Query with text.
Symptom: SQ033 "SELECT access denied for column model_id of table DB.DBA.ONNX_MODELS"
- Cause: A non-DBA account searched by text with
X_SIMILARorVECTOR_DISTANCE. A text search reads the model registry, andSELECTon the table does not cover that. - Fix: Give the account the
SPARQL_SELECTrole, orGRANT SELECT (model_id, model_name, model_type, handle) ON DB.DBA.ONNX_MODELS TO "<account>". See Permissions.
Build performance issues
Symptom: a large REFRESH takes hours and appears to hang
Usually one of the following, and most of them are configurable.
Embedding is running on one core. A serial embedding caller holds a single ONNX helper at a time, so aggregate helper CPU sits at 100% regardless of how many cores the machine has. Raise the concurrency:
registry_set ('__vec_embed_concurrency', '8'); -- default 4, max 32
SELECT DB.DBA.VEC_INDEX_EMBED_CONCURRENCY (); -- confirm the effective valueVerify it took effect by sampling helper CPU while the refresh runs — the aggregate across onnx_helper processes should be several hundred percent, not ~100%. The backfill starting line in the server log also reports the concurrency the refresh actually used. If it could not raise the ONNX helper pool to match, the refresh's result message and SVI_LAST_ERROR say so (could not raise ONNX setting helper_pool_size …) — ONNX_CONFIG is DBA-only, so a refresh run by another account cannot resize the pool.
The useful ceiling is the number of physical cores: each helper is single-threaded, and on a 9.7M-row build 99.998% of the embedding time was model inference. Each concurrent batch also needs a thread of the server's async-queue pool (AsyncQueueMaxThreads, 10 in the stock virtuoso.ini); asked for more, the refresh uses what the pool has and says so in its result — see Embedding concurrency.
The texts are of mixed length. The helper sorts each request by token count, so a larger batch lets labels and paragraphs run apart instead of padding every label to the longest paragraph: on a mixed corpus (measured with all-MiniLM-L6-v2), batch 256 was 1.85 times as fast as the default 32. ONNX_MODEL_INFO's real_tokens / padded_tokens says how much of the model's work was padding. See Batch size.
The refresh is not silent — look at the server log. Progress is reported about every 2000 embedded rows with a percentage, rate and ETA:
vector index VECIDX_...: embedded 2048 of 3000 rows to embed (68%), 884.7 rows/s, ~1 s remainingIf you see no such lines, check whether __vec_progress_every has been set to '0'. Each phase also logs its own time (phase enumerate took ... ms), so the last phase line shows where a refresh is.
The graph build fell back to the serial builder. Look for auto-rebuild falling back to replay in the log. Above about 1.2 million 384-d rows the fast builder's memory estimate exceeds its default 2 GB cap, and the replay builder takes days on a corpus that size; a logged rebuild whose transaction log would not fit under TransactionAfterImageLimit falls back the same way (run it after log_enable (2) — see Rebuild methods). Raise the cap and, on a large machine, the thread count, then rebuild:
registry_set ('__vec_fast_rebuild_max_bytes', '20000000000');
registry_set ('__vec_rebuild_threads', '16');See Operating a multi-million-row build.
Symptom: a small change to a bulk-built index takes as long as the first build
A refresh of a build = 'bulk' index links a change of up to __vec_bulk_delta_pct percent of the graph (default 10) into the existing graph, and logs linking them into the graph instead of rebuilding it; a larger change rebuilds the whole graph. If you see no such line for a small change, check the setting — '0' disables linking — and whether the graph was healthy: a refresh always rebuilds an empty or condemned graph. See Later refreshes of a bulk index.
See Embedding concurrency and Build progress.
Symptom: clearing a registry setting made things slower, not default
registry_get returns a blank string for a key cleared with registry_set (k, ''), which is not the same as an unset key. Both are treated as "use the default" by VEC_INDEX_EMBED_CONCURRENCY and VEC_INDEX_PROGRESS_EVERY, but confirm rather than assume:
registry_set ('__vec_embed_concurrency', '');
SELECT DB.DBA.VEC_INDEX_EMBED_CONCURRENCY (); -- expect 4, not 1Symptom: a remote provider build is extremely slow
Check provider_max_batch. If it is 1, every text costs a separate HTTPS round trip — 150–300 ms each. null uses the per-provider default (128 for OpenAI, 32 otherwise); see Request batching.
SELECT model_name, provider_max_batch FROM DB.DBA.ONNX_MODELS
WHERE model_type <> 'text_embedding';HTTP provider issues
The error a caller sees from a failed provider call is deliberately generic, because a provider's response can echo the text being embedded:
HTTP embedding provider request failed after 1 attempt(s); see the server log for the provider response.(… batch request (N inputs) failed after … for a batch.) The server log has one line per failed attempt, with the provider's status line:
ONNX HTTP embedding provider <provider> model=<model> endpoint=<url> attempt 1/3 failed: HTTP/1.0 401 UnauthorizedThe model's row in DB.DBA.ONNX_MODELS also records the failure, in provider_consecutive_failures and provider_last_error.
Symptom: the provider answers 401 Unauthorized
- Cause: The provider rejected the key. Either the stored key is wrong, or the model's key name points at a registry entry that was never set — the request still goes out, with an invalid key, and the provider answers 401. A 401 (like any 4xx other than 429) is not retried.
- Diagnosis: - Check which key name the model uses:
SELECT provider_api_key_name FROM DB.DBA.ONNX_MODELS WHERE model_name = '...'- Check that a key is stored under that name:SELECT registry_get ('ONNX_API_KEY_OPENAI')returns0when nothing is stored there - Fix: The
_api_key_nameparameter inONNX_REGISTER_HTTP_EMBED_MODELmust be the REGISTRY NAME (e.g.'ONNX_API_KEY_OPENAI'), not the key value itself. For the'openai'provider, passingnullauto-defaults to'ONNX_API_KEY_OPENAI'. (Anopenaimodel whose key is stored as an empty string fails before sending anything, withOpenAI embedding requires a configured API key.)
Symptom: the provider answers 429 Too Many Requests, or 5xx
- Cause: API rate limit exceeded, or the provider failing. A 429, 500, 502, 503 or 504 — and a connection failure or timeout — is retried: up to three attempts, waiting as long as a numeric
Retry-Afterheader asks (at most 30 s), otherwise 0.5 s and then 1 s. The caller sees an error only when all three attempts fail (failed after 3 attempt(s)). - Fix: Reduce the rate of embedding calls — drain the queues less often or in smaller batches, and lower
__vec_embed_concurrencyfor a refresh. Consider using a local model for development.
Symptom: Remote provider query is slow
- Cause: Each query makes an HTTP round-trip to embed the query text —
ORDER BY VECTOR_DISTANCE (col, 'text')makes two,WHERE X_SIMILAR (col, 'text') > …one. - Fix: Use the explicit-vector form, which embeds once:
SELECT TOP 10 id, content,
VECTOR_DISTANCE(content, qv.v) AS distance
FROM DB.DBA.DOCS
CROSS JOIN (SELECT DB.DBA.ONNX_EMBED_TEXT_FOR_MODEL('openai-small', 'query') AS v) qv
ORDER BY VECTOR_DISTANCE(content, qv.v);SPARQL-specific issues
Symptom: VEC58 or VEC59 error
- Cause: The
indexoption was omitted frombif:vector_search, and either no active index (VEC58) or several active indexes (VEC59) exist:Output VEC59: bif:vector_search requires an INDEX option because 4 active vector indexes are available - Fix: Always specify the index:
option (index <urn:your-index>, ...).
To confirm that a query is answered by the vector index rather than as an ordinary triple pattern, look at the SQL it compiles to:
SELECT CAST(sparql_to_sql_text('select ?doc where { ?doc bif:vector_search "test" option (index <urn:docs:idx>, limit 5) }') AS VARCHAR);
-- Should contain DB.DBA.VEC_INDEX_SEARCH_ROWS, not an RDF_QUAD scanSymptom: No rows returned for a non-DBA user
- Cause: Graph ACL — the user doesn't have read permission for the indexed graph
- Fix: Grant read permission:
DB.DBA.RDF_GRAPH_USER_PERMS_SET('urn:docs:g', 'username', 1)
Symptom: VEC72 "Unsupported reduce mode"
- Cause: The
reduceoption in a SPARQLCREATE VECTOR INDEXwas set to a value other than'none'or'truncate'— for examplereduce = 'halve'givesUnsupported reduce mode: halve (expected none or truncate) - Fix: Use
reduce = 'truncate'(Matryoshka reduction) or omit the option (defaults to'none'). See Index parameters: Matryoshka reduction.
Symptom: VEC73 "reduce = truncate requires WITH (dimensions = \<n\>)"
- Cause:
reduce = 'truncate'was specified without an explicitdimensionsvalue in theWITHclause - Fix: Add
dimensions = <n>where<n>is the desired reduced dimension (must be less than the model's native output dimension).
Symptom: VEC74 "reduce = truncate requires dimensions (N) to be less than the model native output dimension (M)"
- Cause:
reduce = 'truncate'was used withdimensions>= the model's native output dimension - Fix: Set
dimensionsto a value strictly less than the native dimension. For example, with OpenAItext-embedding-3-small(1536 native), usedimensions = 256ordimensions = 512.
Symptom: VEC75 "dimensions (N) must equal the model native output dimension (M) when reduce is not truncate"
- Cause:
reduce = 'none'(the default) was used withdimensionsthat does not match the model's native output dimension - Fix: Either set
dimensionsto the model's native dimension, or usereduce = 'truncate'with a smallerdimensionsvalue.
Symptom: VEC76 "Vector index <...> not found for trigger setup"
- Cause: The internal
VEC_INDEX_SET_TRIGGERSprocedure was called for an index IRI that does not exist inDB.DBA.SPARQL_VEC_INDEX. This should not happen during normalCREATE VECTOR INDEXflow; it indicates the catalog row was removed while the index was being created. - Fix: Re-run
CREATE VECTOR INDEX. If it persists, check for orphaned trigger rows:SELECT * FROM DB.DBA.SPARQL_VEC_INDEX_TRIGGER WHERE SVT_INDEX_IRI NOT IN (SELECT SVI_IRI FROM DB.DBA.SPARQL_VEC_INDEX)and delete them.
Symptom: VEC77 "maintenance = async for a general (KEY/EMBEDDING/WHERE) vector index requires every indexed triple pattern to be scoped to a named graph"
- Cause: A general (
KEY/EMBEDDING/WHERE) selector was created withmaintenance = 'async', but theWHEREpattern contains a triple that is neither inside an explicitGRAPH <iri>nor covered by aFROM <iri>clause on the selector (or sits in a variable graph with noFROM). The parser could not extract a(graph, predicate)dependency pair for that triple, so the async change-matcher would have nothing to match against. The create fails and leaves no index behind. - Fix: Wrap every triple pattern in an explicit
GRAPH <iri>block, or add aFROM <iri>clause naming the graphs the pattern reads, and retry — or usemaintenance = 'manual'with periodicREFRESH VECTOR INDEX. See Maintenance: async scoping.
Upgrade issues
Symptom: a fix in a new server build has no effect on an existing database
- Cause: The vector-index procedures (
DB.DBA.VEC_INDEX_*,DB.DBA.VEC_SQL_*,DB.DBA.ONNX_*) are compiled into the server and installed afresh on every start; they are not stored in the database. But a procedure created withcreate procedure— for example by loading a patchedsparql_vec_index.sqlthroughisql— is stored, and a stored procedure takes precedence over the built-in of the same name, across restarts and upgrades. The database keeps running the hand-loaded copy. - Diagnosis: on an unmodified database this returns nothing; anything it returns is a stored copy.
SELECT P_NAME FROM DB.DBA.SYS_PROCEDURES
WHERE P_NAME LIKE 'DB.DBA.VEC!_INDEX%' ESCAPE '!'
OR P_NAME LIKE 'DB.DBA.VEC!_SQL%' ESCAPE '!'
OR P_NAME LIKE 'DB.DBA.ONNX!_%' ESCAPE '!';- Fix: drop the stored copies, then restart the server. The built-in versions are installed only at start-up: between the
DROP PROCEDUREand the restart the procedure does not exist at all (SR185: Undefined procedure), so do this in a maintenance window. Alternatively, load the.sqlfile that matches the new build, which replaces the stored copies with current ones.
Durability and rebuild issues
Symptom: VEC80 "vector index is inconsistent with its stored embeddings and cannot serve searches"
- Cause: The index's stored HNSW graph no longer matches its stored embeddings — typically after an unclean shutdown mid-write. When the graph has only fallen behind the data, the server rebuilds it automatically on first use and the server log says so;
VEC80then means the search arrived while that rebuild was running, or the index could not be repaired automatically and has been markedneeds_rebuild. Either way it fails closed: it refuses to serve searches rather than return silently degraded results. - Diagnosis:
SELECT VIE_NAME, VIE_STATE, VIE_EPOCH, VIE_CHECKED_AT FROM DB.DBA.VEC_INDEX_EPOCH;
-- VIE_STATE = 'needs_rebuild' identifies the affected index- Fix: Rebuild the graph from the durable stored embeddings (no re-embedding or model call is needed):
SELECT __VEC_INDEX_REBUILD('DOCS_VEC');
SELECT __VEC_INDEX_HEALTH_RECHECK('DOCS_VEC'); -- expect 1 = okFor a SPARQL index, REFRESH VECTOR INDEX <iri> recovers it too: a refresh that finds the graph unhealthy rebuilds it. See Maintenance: durability and recovery.
- Only a rebuild clears a
needs_rebuildverdict. It is stored, so it survives a restart, and neither a repeated__VEC_INDEX_HEALTH_RECHECKnor inserts and deletes into the index clear it.
Symptom: VEC95 "a logged fast rebuild ... would fail at commit"
- Cause: An explicit
fastrebuild —__VEC_INDEX_REBUILD (idx, 'fast'), or an index createdWITH (rebuild = 'fast')— was started with the transaction log on, and even a lower bound on the log it would write exceeds what is left ofTransactionAfterImageLimit(50 MB by default). A transaction whose log outgrows the limit fails at commit, after the whole build, so the rebuild is refused before the old graph is touched, and the old graph keeps answering:Output VEC95: Vector index FA_IDX: a logged fast rebuild of 31000 vectors writes at least 51584000 bytes of transaction log, more than the 50000000 bytes left under TransactionAfterImageLimit, so it would fail at commit after building the whole graph. Run it after log_enable (2) and checkpoint afterwards, or raise TransactionAfterImageLimit.The bound is a floor, so an explicit logged fast rebuild that passes it can still fail at commit with
SR325when it ends up just over the limit. auto, and abuild = 'bulk'refresh, do not get this error: when a logged fast build would not fit, they use the replay rebuild, which commits as it goes, and say so in the server log:
Vector index FA_IDX: auto-rebuild falling back to replay -- a logged fast build of 31000 vectors (dim 384) would write about 53816000 bytes of transaction log, more than the 50000000 bytes left under TransactionAfterImageLimit; replay commits as it goes. Rebuild after log_enable (2) and checkpoint afterwards to use the fast builder.- Fix: Run it unlogged, then checkpoint:
log_enable (2, 1);
SELECT __VEC_INDEX_REBUILD('DOCS_VEC', 'fast');
COMMIT work;
log_enable (1, 1);
CHECKPOINT;Or raise TransactionAfterImageLimit in virtuoso.ini, if you want the rebuild in the log.
Symptom: VEC94 "cannot allocate fast-rebuild input arena"
- Cause: The fast builder allocates room for every stored vector before it starts, and the allocation failed. Nothing was changed: the old graph still answers.
- Fix: Free memory, or rebuild with
'replay'— slower, but it holds one node at a time. See the memory guard under Maintenance: rebuild methods.
Symptom: VEC81 "method must be 'replay', 'fast' or 'auto'"
- Cause:
__VEC_INDEX_REBUILD(__VEC_INDEX_REBUILD method must be 'replay', 'fast' or 'auto'.) or therebuildoption onCREATE VECTOR INDEX(Vector rebuild option must be 'replay', 'fast' or 'auto'.) was given a method other thanreplay,fast, orauto. There is nolegacymethod —legacyandpackedrefer to the on-disk page format, not the rebuild method. - Fix: Use a valid method:
SELECT __VEC_INDEX_REBUILD('DOCS_VEC', 'fast');. See Maintenance: rebuild methods.
Performance issues
Symptom: Index creation is slow
- Cause: Embedding the rows, a high
ef_constructionvalue, or a large dataset - Fix: With a local model, embedding usually dominates — see Build performance issues. The graph build scales as O(N * log(N) * ef_construction): the default
ef_construction=200suits development, and 400 is a good balance for production. Values above 1000 have diminishing returns.
Symptom: Search latency is high
- Cause: High
ef_search/k, large dataset, or high-dimensional vectors - Fix: - Reduce
k(SQL) oref_search(SPARQL) — but this may reduce recall - Check__VEC_INDEX_METRICSforsearch_usec/search_countto see average latency (searches are timed to the millisecond, so on a small index the average reads 0) - For remote providers, network latency dominates — consider a local model
Symptom: High memory usage
- Cause: HNSW graph memory + ONNX helper process
- Fix: - Reduce
hnsw_m(fewer neighbors per node) - Use a lower-dimensional model - Usereduce = 'truncate'with an MRL-trained model (e.g. OpenAI text-embedding-3-small 1536 → 256) to reduce per-vector memory by up to 6x - ReduceNumberOfBuffersinvirtuoso.ini- Budget for the ONNX helpers: two or more per local model, up to about 285 MB each at rest with the bundled model (see Deployment and sizing)
Diagnostic functions quick reference
| Function | Purpose |
|---|---|
__VEC_INDEX_METRICS('idx') | Index health: node counts, search stats, state |
__VEC_INDEX_PAGE_STATS('idx') | Storage stats: page counts, entry point, layers |
__VEC_INDEX_VALIDATE('idx') | ANN vs exact recall, and graph/data consistency |
__VEC_INDEX_HEALTH_RECHECK('idx') | Re-evaluate health: 1 = ok, 2 = needs_rebuild |
__VEC_INDEX_REBUILD('idx' [, method [, threads]]) | Rebuild graph from stored embeddings; recovers a needs_rebuild (VEC80) index |
__VEC_INDEX_QUERY_TEXT_VECTOR_CALLS(reset) | Count of query texts embedded, server-wide; a non-zero reset also clears it |
DB.DBA.VEC_SQL_WORKER_STATUS() | SQL queue health (a name/value vector: SELECT obj2json (...)) |
DB.DBA.VEC_INDEX_WORKER_STATUS() | SPARQL queue worker health |
explain('SELECT ...') | Check if ANN or full scan is used |
sparql_to_sql_text('...') | Check that bif:vector_search compiles to VEC_INDEX_SEARCH_ROWS |
The __VEC_INDEX_* functions in this table are DBA-only. Those that return name/value vectors read best through obj2json.
Next steps
- Maintenance — Learn how to drain maintenance queues, schedule workers, and keep indexes current as data changes
- API reference — Reference for all vector index procedures, BIFs, and catalog tables
- SQL vector index how-to — Complete guide for creating, querying, and maintaining SQL vector indexes
Related content
Reference and concepts:
- Embedding models — Local ONNX models, remote HTTP providers, API key management, and model registration
- Index parameters — Tune HNSW parameters (
hnsw_m,ef_construction) and query-time settings (k,ef_search) for accuracy and performance - Vector index overview — Architecture and concepts behind the Virtuoso vector index
PreviousManaging vector indexes visually with the VVec Console NextAPI reference for the Virtuoso vector index