Was this page helpful?
Vector and Text Search Troubleshooting¶
This page lists common issues encountered when working with Vector and Text Search in ScyllaDB and provides solutions for each.
Index Issues¶
Index creation fails with a tablets error¶
Symptom: CREATE CUSTOM INDEX returns an error about tablets not
being enabled.
Cause: The table’s keyspace was created without tablets support.
Solution: Vector indexes require tablets-enabled keyspaces. Create a new keyspace with tablets enabled and recreate the table:
CREATE KEYSPACE myapp;
See Tablets Requirement.
Index creation is slow¶
Symptom: CREATE CUSTOM INDEX takes a long time on a table with
existing data.
Cause: When creating an index on a table that already contains data, ScyllaDB must build the HNSW graph over all existing vectors in a background process.
Solution: This is expected behavior. Build time depends on the number of
rows and vector dimensionality. Monitor progress through the
Monitoring dashboard. For large datasets, consider
increasing construction_beam_width for higher-quality builds, or lowering
it to speed up construction at the expense of recall.
Cannot change index options after creation¶
Symptom: ALTER INDEX is not supported.
Cause: ScyllaDB does not support altering vector index options.
Solution: Drop the existing index and recreate it with new options:
DROP INDEX IF EXISTS myapp.my_index;
CREATE CUSTOM INDEX my_index ON myapp.my_table(vec)
USING 'vector_index'
WITH OPTIONS = { 'similarity_function': 'COSINE' };
Query Issues¶
ANN query returns no results¶
Symptom: ORDER BY ... ANN OF ... LIMIT k returns an empty result set.
Possible causes:
The vector index has not finished building.
No data has been inserted into the table.
The query vector dimensionality does not match the column definition.
Solution: Verify:
The index status is
ACTIVE(check viaDESCRIBE INDEX).Data exists in the table (
SELECT COUNT(*) FROM table).The query vector has the correct number of dimensions.
ANN query returns stale results¶
Symptom: Recently inserted vectors do not appear in ANN query results.
Cause: Vector indexes are updated asynchronously via CDC. There is a short propagation delay (typically under 1 second, up to 30 seconds in edge cases).
Solution: Wait briefly and retry. See Write-to-Query Latency for details on the dual CDC reader system.
Query returns unexpected results¶
Symptom: The top-k results seem irrelevant or have low similarity.
Possible causes:
The similarity function used in the index does not match the embedding model’s output characteristics.
The embedding model was changed after data was inserted, producing vectors in a different space.
search_beam_widthis set too low, reducing recall.
Solution:
Verify the similarity function matches your model. See Choosing a Similarity Function.
Ensure all vectors in the table use the same embedding model.
Increase
search_beam_widthto improve recall (requires dropping and recreating the index).
Data Issues¶
Insert fails with a vector dimension mismatch¶
Symptom: CQL INSERT returns an error about vector dimensions.
Cause: The number of elements in the vector literal does not match the column’s declared dimension.
Solution: Ensure the vector has exactly the number of elements declared
in the schema. For example, if the column is vector<float, 768>, every
inserted vector must have exactly 768 elements.
TRUNCATE TABLE corrupts the vector index¶
Symptom: After running TRUNCATE TABLE, ANN queries continue to return
old results even though the table is empty, or query behavior is otherwise
incorrect.
Cause: TRUNCATE does not generate CDC events. Because the vector index
is updated exclusively through CDC, the truncation is invisible to the index —
the table is emptied but the HNSW graph still contains all previous vectors.
Solution: Do not use TRUNCATE on tables with vector indexes. Instead,
drop and recreate both the table and the custom index:
DROP TABLE IF EXISTS myapp.my_table;
CREATE TABLE myapp.my_table (...);
CREATE CUSTOM INDEX my_index ON myapp.my_table(vec)
USING 'vector_index'
WITH OPTIONS = { ... };
See CQL Features Not Supported for the full list of limitations.
ANN query returns fewer results than LIMIT after deletes¶
Symptom: After deleting rows with DELETE FROM t WHERE pk = ? or
DELETE FROM t WHERE pk = ? AND ck > ?, ANN queries return fewer rows
than the requested LIMIT.
Cause: This issue only affects tables that have a clustering key. Partition
deletes (where only the partition key is specified) and range deletes (where
the clustering key uses an inequality operator like >, <, >=,
<=) are not propagated to the vector index. The CDC events for these
operations do not contain full clustering key values, so the vector search CDC
reader cannot identify the specific rows to remove from the index. ScyllaDB
filters out the deleted rows at query time, so they do not appear in results —
but because they still occupy candidate slots in the index, the final result
set may contain fewer rows than LIMIT. Additionally, the stale entries
continue to consume memory on the indexing nodes.
Solution: Always delete rows from tables with vector indexes using a fully specified primary key (all partition key and clustering key columns). For example:
-- Supported: fully specified primary key
DELETE FROM t WHERE pk = 1 AND ck = 1;
-- NOT supported: partition delete (no clustering key)
DELETE FROM t WHERE pk = 1;
-- NOT supported: range delete (inequality on clustering key)
DELETE FROM t WHERE pk = 2 AND ck > 2;
If you have already issued partition or range deletes, drop and recreate the vector index to force a full rebuild from the current table state. See CQL Features Not Supported for the full list of limitations.
Rows written with USING TTL never leave the vector index¶
Symptom: Rows written with USING TTL, or on a table with
default_time_to_live set, have expired and no longer appear in SELECT
results, but the vector index size does not shrink, memory usage on the
indexing nodes keeps growing, and ANN queries return fewer rows than the
requested LIMIT.
Cause: Cell-level TTL is not supported on tables with vector indexes.
ScyllaDB accepts the write and the data expires from the base table, but cell
expiration is passive — expired cells are discarded at read time and during
compaction without issuing a delete, so no CDC event is generated. Because the
vector index is maintained exclusively through CDC, the vector stays in the
HNSW graph. The expired data does not reappear in results, but the stale entries
occupy candidate slots during the index search and continue to consume memory.
If a TTL covers the vector column without expiring the whole row, the row can
also survive with a null vector while the index still holds its old value.
Solution: Use per-row TTL instead, which stores an absolute expiration time in a dedicated column. The background expiration service issues real row deletes, and those deletes do propagate to the index through CDC:
-- Supported: per-row TTL through a dedicated expiration-time column.
CREATE TABLE myapp.sessions (
id uuid PRIMARY KEY,
embedding vector<float, 5>,
expire_at bigint TTL -- seconds since the UNIX epoch
);
-- NOT supported: cell-level TTL on an individual write.
INSERT INTO myapp.sessions (id, embedding)
VALUES (uuid(), [0.1, 0.15, 0.3, 0.12, 0.05]) USING TTL 3600;
-- NOT supported: a table-wide default TTL.
ALTER TABLE myapp.sessions WITH default_time_to_live = 3600;
If the table has already been written with cell-level TTL, drop and recreate the vector index to rebuild the HNSW graph from the current table state. See Cell-level TTL is not supported for details.
Connectivity Issues¶
Cannot connect to the cluster¶
Symptom: Driver or cqlsh cannot connect to the cluster.
Solution:
Verify your cluster is in
ACTIVEstatus in the ScyllaDB Cloud console.Ensure your client IP is allowed in the cluster’s connection settings.
Verify TLS is enabled in your driver configuration (ScyllaDB Cloud requires TLS).
Check that the DC-aware load balancing policy is configured correctly.
See Checking Cluster Availability for connection troubleshooting.
Performance Issues¶
Memory pressure or OOM on indexing nodes¶
Symptom: Queries time out or indexing nodes restart unexpectedly.
Cause: The vector index size exceeds available RAM on the indexing nodes. The HNSW index resides entirely in memory, so under-provisioned instances will experience memory pressure.
Solution:
Use quantization (f16 or i8) to reduce memory per vector. See Quantization and Rescoring.
Choose a larger instance type with more RAM. See Supported Instance Types.
Use the Sizing Guide to estimate memory requirements before scaling up.
Performance degradation during heavy writes¶
Symptom: Query latency increases during bulk data loading or heavy write periods.
Cause: The CDC readers that propagate changes to indexing nodes consume additional CPU and memory when processing a high volume of changes. This is expected behavior — the index is being continuously updated.
Solution:
This is temporary. Query latency returns to normal once the write burst completes and the CDC backlog is processed.
For planned bulk loads, consider loading data before creating the index, so the HNSW graph is built in a single pass rather than incrementally.
Monitor the Write-to-Query Latency — during heavy writes, propagation latency may increase from sub-second to several seconds.
Filtering query returns fewer results than expected¶
Symptom: A SELECT ... WHERE ... ORDER BY ... ANN OF ... LIMIT 10
query returns fewer than 10 rows.
Cause: The filter is highly selective and not enough matching vectors
exist in the candidate set. The ANN search first finds the nearest vectors,
then applies the filter - if the filter eliminates most candidates, fewer
results remain. This is especially pronounced with global indexes (which must
search the entire index space) and with inequality (>=, <=, etc.) or
IN operators, where the slowdown is proportional to selectivity.
Solution:
Increase the
LIMITto a higher value to give the search more candidates to work with.Use a less selective filter condition.
Switch to a local (per-partition) vector index with equality (
=) filters on partition key columns - this is the fastest filtering path. See Filtering.
Driver version incompatibility¶
Symptom: The driver cannot parse vector column responses, or vector inserts fail with type errors.
Cause: The VECTOR data type requires driver support. Older driver
versions may not recognize the vector type.
Solution: Upgrade to a driver version that supports vectors. See ScyllaDB Drivers — Support for Vector Search to check which versions support the vector type.
CDC is active and cannot be disabled¶
Symptom: A table you never configured for CDC has a companion
<table>_scylla_cdc_log table, but DESCRIBE TABLE shows no
cdc = {...} property to explain it. Disabling CDC fails while a vector
index exists, and CREATE CUSTOM INDEX fails on a table where CDC was
explicitly disabled.
Cause: A vector index reads the base table’s CDC log, so CDC must be active
for the index to receive updates (see
CDC-Based Indexing). When you create a vector index on
a table with no CDC configuration, the index itself is what makes CDC active:
ScyllaDB does not write a cdc property to the schema. That is why
DESCRIBE TABLE shows no cdc = {...} line and
system_schema.scylla_tables.cdc is null. The only visible evidence is
the <table>_scylla_cdc_log table, which DESCRIBE emits with the comment
CDC log for <keyspace>.<table>. The effective settings are the CDC
defaults, which already meet the Vector and Text Search minimum: a TTL of 24
hours and delta mode full.
Solution: Match the case to the error you received.
To disable CDC — drop all vector indexes on the table first. While an index exists, disabling CDC fails with
Cannot disable CDC when Vector Search is enabled on the table:DROP INDEX myapp.ann_idx; ALTER TABLE myapp.comments WITH cdc = {'enabled': false};
To keep the vector index — leave CDC enabled. CDC must stay enabled for the index to receive updates.
If index creation failed on the CDC options — the error reports that
the CDC log must meet the minimal requirements for external indexes. Raise the TTL to at least 24 hours (or0for infinite retention) and use delta modefullor enablepostimage, then create the index:ALTER TABLE myapp.comments WITH cdc = {'enabled': true, 'ttl': '86400', 'delta': 'full'};
If index creation failed because CDC was explicitly disabled — the error is
Cannot create the vector index when CDC is explicitly disabled. Creating an index does not re-enable CDC for you. Re-enable it with the statement above, then create the index.If you lowered the CDC options while an index exists — the same minimum requirements are enforced on
ALTER TABLE, so the statement is rejected. Keep the TTL at 24 hours or more and delta modefull(orpostimageenabled) for as long as any vector index exists on the table.
Caution
Every ALTER TABLE ... WITH cdc = {...} statement must include the
enabled flag; omitting it fails with
Altering CDC options requires specifying "enabled" flag. Options you
leave out are reset to their defaults instead of being merged with the
current values, so always specify the full set of CDC options you want to
end up with.
See CDC Requirements for Vector Indexes for the full set of requirements.
Index build progress monitoring¶
Symptom: You created an index on a table with existing data and want to know if the index has finished building.
Cause: When an index is created on a table that already contains data, ScyllaDB builds the HNSW graph as a background process. Until the build completes, ANN queries may return incomplete results.
Solution:
Check the index status using
DESCRIBE INDEX— the status should beACTIVEwhen the build is complete.Monitor the indexing node metrics in the Monitoring dashboard.
Build time depends on the number of rows, vector dimensionality, and the
construction_beam_widthparameter. Larger datasets with higher dimensions take longer.
Full Text Search Issues¶
Full text search query is rejected as invalid¶
Symptom: A BM25() query fails with an InvalidRequest error such as
Full-text search queries require a LIMIT or Full-text search queries do
not support additional WHERE restrictions.
Cause: Full text search accepts exactly one query shape, and rejects any deviation from it.
Solution: Write the query as:
SELECT id, body FROM blog.articles
WHERE BM25(body, 'search terms') > 0
ORDER BY BM25(body, 'search terms')
LIMIT 10;
ORDER BY and LIMIT are both mandatory, LIMIT must not exceed 1000,
the search term must be identical in both clauses, the comparison must be
> 0, and no other WHERE restriction is allowed. See
Running Full Text Search Queries for the full list of rules and the error each one
produces.
Full text search query returns no results¶
Symptom: A term you can see in the stored text matches nothing.
Cause and solution: In order of likelihood:
The word form differs. The default
standardanalyzer does not stem, so a term matches only in the form in which it appears —scaledoes not match the text scales. Search for the stored form, combine forms withOR, or recreate the index with a language analyzer such asenglish. See Choosing an Analyzer.The term is a stop word. The
standardand language analyzers drop common words, so searching fortheon its own matches nothing. Thesimpleanalyzer keeps them. See How Text Is Analyzed.The query used fuzzy or prefix syntax.
term~1andterm*are parsed without error but match nothing.The write has not been indexed yet. Updates reach the index asynchronously through CDC and take a few seconds. See Write-to-Query Latency.
Full text search query fails to parse¶
Symptom: The query fails with failed to parse query: Syntax Error:
<input> or failed to parse query: Field does not exist: <input>.
Cause: The string passed to BM25() is a query expression, not a literal
phrase. Characters such as :, [, {, and ' are meaningful to the
parser, and a bare AND, OR, or NOT is incomplete.
Solution: Sanitize end-user input before searching. Note that a bound parameter protects against CQL injection but is still parsed as a query expression, so it does not help here. See Unsupported Query Syntax for a worked example.
Phrase query fails with “does not have positions indexed”¶
Symptom: A quoted phrase query fails with:
Vector Store error: HTTP status 400 Bad Request, message: index.bm25 request
error: fts: failed to parse query: The field 'body' does not have positions
indexed
Cause: The index was created with 'positions': 'false', which omits the
token positions that phrase queries match on. Term and boolean queries against
the same index still work.
Solution: Drop the index and recreate it with the default
'positions': 'true', or rewrite the query without the quotes so the words
are matched as independent terms. See Phrase Queries and the positions Option.
Wrong language behaviour: stop words kept or inflections not matching¶
Symptom: On non-English text, common words are indexed and searchable when you expected them to be dropped, and different forms of the same word do not match each other.
Cause: The index is using the default standard analyzer, which removes
English stop words and performs no stemming. On German text, for example,
it indexes die and does not match lauf against laufen.
Solution: Recreate the index with the analyzer for your language — the
german analyzer drops die and stems laufen to lauf. See
Choosing an Analyzer.
Full text search returns fewer rows than LIMIT after TTL expiration¶
Symptom: A BM25() query returns far fewer rows than the LIMIT, and
fewer than the number of live rows that match the term. Memory on the indexing
nodes does not drop as rows expire.
Cause: The rows were expired by cell-level TTL — USING TTL on an
INSERT or UPDATE, or the default_time_to_live table property.
Neither generates a CDC event, so the indexing node is never told the rows are
gone and the full-text index keeps an entry for each one. ScyllaDB filters the
expired rows out of the results, so what comes back is correct, but the stale
entries still consume candidate slots against the LIMIT. Where expired
rows outnumber live ones, a query may return only a small fraction of the
matching rows.
Solution: Use per-row TTL on full-text-indexed tables instead — it deletes the row explicitly, which emits a CDC event and removes the index entry. To clear entries already stranded by cell-level TTL, drop and recreate the index so it is rebuilt from the current table contents. See Cell-Level TTL Is Not Supported.
Only part of the matching rows is returned¶
Symptom: A common term matches far more rows than the query returns, and the result set stops at 1000.
Cause: LIMIT is mandatory and capped at 1000, and full text search
results are not paged.
Solution: This is expected. Narrow the query with additional terms rather than trying to page through the matches. See Result Size and Paging.
What’s Next¶
Working with Vector and Text Search — CQL syntax reference for vector tables, indexes, and queries.
Working with Full Text Search — CQL syntax reference for full-text indexes and
BM25()queries.Vector and Text Search Concepts — architecture and design principles.
Reference — instance types, CQL reference, and API endpoints.