You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by al...@apache.org on 2026/08/25 02:22:44 UTC

(asterixdb) branch master updated: [ASTERIXDB-3760][OTH] Add coded error messages for vector idx

This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git


The following commit(s) were added to refs/heads/master by this push:
     new e9a8091ce2 [ASTERIXDB-3760][OTH] Add coded error messages for vector idx
e9a8091ce2 is described below

commit e9a8091ce205ab485d830359bf085d689444406e
Author: Ali Alsuliman <al...@gmail.com>
AuthorDate: Thu Aug 20 17:13:21 2026 -0700

    [ASTERIXDB-3760][OTH] Add coded error messages for vector idx
    
    - user model changes: no
    - storage format changes: no
    - interface changes: no
    
    [ASTERIXDB-3760][ASTERIXDB-3771][ASTERIXDB-3754]
    
    Ext-ref: MB-73194, MB-72975
    Co-Authored-By: Claude Opus 5 <no...@anthropic.com>
    Change-Id: I2c9bc14fcaa8c850abda77cc320cddbd3c254f61
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/21586
    Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Reviewed-by: Ali Alsuliman <al...@gmail.com>
    Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Reviewed-by: Ian Maxon <im...@apache.org>
---
 .../operators/physical/VectorSearchPOperator.java  |  5 ++-
 .../rules/am/VectorIndexAccessMethod.java          |  7 ++-
 .../asterix/common/exceptions/ErrorCode.java       |  6 ++-
 .../vector/OptimizedScalarQuantizationCodec.java   | 52 ++++++++++++----------
 .../vector/OptimizedScalarQuantizerFactory.java    |  4 +-
 .../src/main/resources/asx_errormsg/en.properties  |  2 +
 .../lang/common/util/VectorIndexDeclUtil.java      |  4 +-
 .../metadata/declared/MetadataProvider.java        |  7 +--
 .../metadata/utils/SampleOperationsHelper.java     |  8 +++-
 .../common/AOrderedListVectorBinaryAccessor.java   | 28 +++++++-----
 .../QuantizationConstantsAggregateDescriptor.java  | 42 ++++++++++-------
 ...lKMeansPlusPlusCentroidsOperatorDescriptor.java | 32 ++++++-------
 .../asterix/runtime/operators/KMeansUtils.java     |  8 +++-
 ...reeBulkLoaderAndGroupingOperatorDescriptor.java | 35 ++++++++++-----
 ...eeStaticStructureCreatorOperatorDescriptor.java | 35 +++++++++------
 ...VectorComponentExtractorOperatorDescriptor.java |  3 +-
 .../runtime/utils/VectorDistanceCalculation.java   |  5 ++-
 .../utils/VectorDistanceFunctionFactory.java       |  2 +-
 .../apache/hyracks/api/exceptions/ErrorCode.java   |  3 ++
 .../src/main/resources/errormsg/en.properties      |  3 ++
 .../DoubleArraySerializerDeserializer.java         | 14 +++---
 .../lsm/vector/dataflow/LSMVTreeLocalResource.java |  8 ++--
 .../QuantizedIndexCreateOperatorDescriptor.java    |  3 +-
 .../dataflow/VTreeSearchOperatorNodePushable.java  |  3 ++
 .../storage/am/lsm/vector/impls/LSMVTree.java      |  8 +++-
 .../am/lsm/vector/impls/LSMVTreeDiskComponent.java | 12 ++---
 .../am/lsm/vector/impls/LSMVTreeSearchCursor.java  |  3 ++
 .../lsm/vector/impls/LSMVTreeTopKSearchCursor.java |  8 +++-
 .../am/lsm/vector/impls/PKOnlyTupleProjector.java  |  6 ++-
 .../vector/api/IVTreeDistanceFunctionFactory.java  |  3 +-
 .../storage/am/vector/frames/VTreeNSMFrame.java    |  4 +-
 .../hyracks/storage/am/vector/impls/VTree.java     | 12 +++--
 .../storage/am/vector/impls/VTreeBulkLoader.java   |  4 +-
 .../storage/am/vector/impls/VTreeFlushLoader.java  |  8 +++-
 .../storage/am/vector/impls/VTreeSearchCursor.java | 16 ++++++-
 .../vector/impls/VTreeStaticStructureBuilder.java  |  6 ++-
 .../am/vector/utils/NoOpVectorQuantizer.java       |  5 ++-
 .../am/vector/TestDoubleArrayVectorAccessor.java   |  4 +-
 38 files changed, 267 insertions(+), 151 deletions(-)

diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/VectorSearchPOperator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/VectorSearchPOperator.java
index 712e8b6b3d..477ec96ff4 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/VectorSearchPOperator.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/VectorSearchPOperator.java
@@ -22,6 +22,8 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.metadata.declared.DataSourceId;
 import org.apache.asterix.metadata.declared.DataSourceIndex;
 import org.apache.asterix.metadata.declared.DatasetDataSource;
@@ -121,7 +123,8 @@ public class VectorSearchPOperator extends IndexSearchPOperator {
         AbstractUnnestMapOperator unnestMap = (AbstractUnnestMapOperator) op;
         ILogicalExpression unnestExpr = unnestMap.getExpressionRef().getValue();
         if (unnestExpr.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
-            throw new AlgebricksException("Vector search unnest expression must be a function call");
+            throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE,
+                    "the vector search unnest expression is not a function call");
         }
         AbstractFunctionCallExpression unnestFuncExpr = (AbstractFunctionCallExpression) unnestExpr;
         FunctionIdentifier funcIdent = unnestFuncExpr.getFunctionIdentifier();
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/VectorIndexAccessMethod.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/VectorIndexAccessMethod.java
index 1a0828ad9c..8fb528ca42 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/VectorIndexAccessMethod.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/VectorIndexAccessMethod.java
@@ -30,6 +30,8 @@ import java.util.Set;
 import org.apache.asterix.common.annotations.AbstractExpressionAnnotationWithIndexNames;
 import org.apache.asterix.common.annotations.AnnSearchPreferenceAnnotation;
 import org.apache.asterix.common.config.DatasetConfig.IndexType;
+import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.common.vector.VectorSimilarityMetric;
 import org.apache.asterix.metadata.entities.Dataset;
 import org.apache.asterix.metadata.entities.Index;
@@ -344,8 +346,9 @@ public class VectorIndexAccessMethod implements IAccessMethod {
                 // there is no falling back to lookup-and-rerank from here. retainInput/retainNull are both
                 // false, which is exactly the case where createSecondaryIndexUnnestMap returns an
                 // UnnestMapOperator, so this is a broken invariant rather than an unsupported plan shape.
-                throw new AlgebricksException("Vector index-only plan: expected an UnnestMapOperator from the "
-                        + "secondary index search but got " + secondaryIndexUnnestOp.getOperatorTag());
+                throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE,
+                        "the vector index-only plan expected an UnnestMapOperator from the secondary index "
+                                + "search but got " + secondaryIndexUnnestOp.getOperatorTag());
             } else {
                 UnnestMapOperator unnestMap = (UnnestMapOperator) secondaryIndexUnnestOp;
                 LogicalVariable distVar = context.newVar();
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
index 0dd540a26f..fdfa61fef4 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
@@ -364,11 +364,13 @@ public enum ErrorCode implements IError {
     UPDATE_INSERT_POSITION_OUT_OF_BOUNDS(1252),
     ASYNC_NOT_SUPPORTED_FOR_STATEMENT(1253),
     UNSUPPORTED_FILE_IO_TYPE(1254),
-    INVALID_FULL_SCAN_OPTION(1255),
+    INVALID_FULL_SCAN_OPTION(1255), // not used, remove if not shipped yet
     INVALID_SAMPLE_METHOD(1256),
-    COMPILATION_ANN_DISTANCE_METRIC_MISMATCH(1257),
+    COMPILATION_ANN_DISTANCE_METRIC_MISMATCH(1257), // not used, remove if not shipped yet
     COMPILATION_VECTOR_INDEX_CREATION_FAILED(1258),
     UNSUPPORTED_MULTIPLE_STATEMENTS_FOR_DELIVERY_MODE(1259),
+    UNSUPPORTED_VECTOR_ELEMENT_TYPE(1260),
+    VECTOR_DIMENSION_MISMATCH(1261),
 
     // Feed errors
     DATAFLOW_ILLEGAL_STATE(3001),
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/vector/OptimizedScalarQuantizationCodec.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/vector/OptimizedScalarQuantizationCodec.java
index 57d6e6412d..ae8d2942be 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/vector/OptimizedScalarQuantizationCodec.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/vector/OptimizedScalarQuantizationCodec.java
@@ -18,6 +18,10 @@
  */
 package org.apache.asterix.common.vector;
 
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.exceptions.RuntimeDataException;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
 /**
  * Optimized scalar quantization (OSQ) utilities for vector indexes.
  *
@@ -147,19 +151,18 @@ public final class OptimizedScalarQuantizationCodec {
      * @param params Quantization parameters including bits, minQuantile, maxQuantile, alpha
      * @param similarityFunction The similarity function type (recorded on the result)
      * @return QuantizedVector containing quantized bytes and metadata
-     * @throws IllegalArgumentException if vector is null or params are invalid
+     * @throws HyracksDataException if vector is null or params are invalid
      */
-    public static QuantizedVector quantizeVector(double[] vector, Params params,
-            SimilarityFunction similarityFunction) {
+    public static QuantizedVector quantizeVector(double[] vector, Params params, SimilarityFunction similarityFunction)
+            throws HyracksDataException {
         if (vector == null) {
-            throw new IllegalArgumentException("vector must be non-null");
+            throw new RuntimeDataException(ErrorCode.ILLEGAL_STATE, "A null vector reached the quantizer");
         }
         if (params == null) {
-            throw new IllegalArgumentException("params must be non-null");
+            throw new RuntimeDataException(ErrorCode.ILLEGAL_STATE, "Null quantization params reached the quantizer");
         }
         if (vector.length != params.vectorDimensions) {
-            throw new IllegalArgumentException(
-                    "vector dimension mismatch: expected " + params.vectorDimensions + ", got " + vector.length);
+            throw new RuntimeDataException(ErrorCode.VECTOR_DIMENSION_MISMATCH, params.vectorDimensions, vector.length);
         }
 
         final int bits = params.bits;
@@ -176,7 +179,8 @@ public final class OptimizedScalarQuantizationCodec {
         } else if (bits <= 32) {
             quantizedBytes = quantizeToInt(vector, minQ, maxQ, alpha, levels);
         } else {
-            throw new IllegalArgumentException("bits must be <= 32, got " + bits);
+            throw new RuntimeDataException(ErrorCode.ILLEGAL_STATE,
+                    "Unsupported quantization bit width " + bits + "; the maximum is 32");
         }
 
         return new QuantizedVector(quantizedBytes, similarityFunction);
@@ -300,15 +304,15 @@ public final class OptimizedScalarQuantizationCodec {
      *                       inverse mapping; bits and vectorDimensions for validation)
      * @return double[] array where each element approximates the original value via
      *         inverse quantization: value = quantizedInt / alpha + minQuantile
-     * @throws IllegalArgumentException if inputs are null or array length doesn't
-     *                                  match params
+     * @throws HyracksDataException if inputs are null or array length doesn't
+     *                              match params
      */
-    public static double[] dequantizeToDoubleArray(Object quantizedBytes, Params params) {
+    public static double[] dequantizeToDoubleArray(Object quantizedBytes, Params params) throws HyracksDataException {
         if (quantizedBytes == null) {
-            throw new IllegalArgumentException("quantizedBytes must be non-null");
+            throw new RuntimeDataException(ErrorCode.ILLEGAL_STATE, "A null quantized vector reached the dequantizer");
         }
         if (params == null) {
-            throw new IllegalArgumentException("params must be non-null");
+            throw new RuntimeDataException(ErrorCode.ILLEGAL_STATE, "Null quantization params reached the dequantizer");
         }
 
         final int dims = params.vectorDimensions;
@@ -319,12 +323,12 @@ public final class OptimizedScalarQuantizationCodec {
         if (bits <= 8) {
             // byte[] - treat as unsigned
             if (!(quantizedBytes instanceof byte[])) {
-                throw new IllegalArgumentException(
-                        "Expected byte[] for " + bits + " bits, got " + quantizedBytes.getClass().getName());
+                throw new RuntimeDataException(ErrorCode.ILLEGAL_STATE, "Expected a byte[] quantized vector for " + bits
+                        + " bits, but got a " + quantizedBytes.getClass().getName());
             }
             byte[] bytes = (byte[]) quantizedBytes;
             if (bytes.length != dims) {
-                throw new IllegalArgumentException("Array length mismatch: expected " + dims + ", got " + bytes.length);
+                throw new RuntimeDataException(ErrorCode.VECTOR_DIMENSION_MISMATCH, dims, bytes.length);
             }
             for (int i = 0; i < dims; i++) {
                 result[i] = ((double) (bytes[i] & 0xFF)) / params.alpha + params.minQuantile;
@@ -332,13 +336,12 @@ public final class OptimizedScalarQuantizationCodec {
         } else if (bits <= 16) {
             // short[] - treat as unsigned
             if (!(quantizedBytes instanceof short[])) {
-                throw new IllegalArgumentException(
-                        "Expected short[] for " + bits + " bits, got " + quantizedBytes.getClass().getName());
+                throw new RuntimeDataException(ErrorCode.ILLEGAL_STATE, "Expected a short[] quantized vector for "
+                        + bits + " bits, but got a " + quantizedBytes.getClass().getName());
             }
             short[] shorts = (short[]) quantizedBytes;
             if (shorts.length != dims) {
-                throw new IllegalArgumentException(
-                        "Array length mismatch: expected " + dims + ", got " + shorts.length);
+                throw new RuntimeDataException(ErrorCode.VECTOR_DIMENSION_MISMATCH, dims, shorts.length);
             }
             for (int i = 0; i < dims; i++) {
                 result[i] = ((double) (shorts[i] & 0xFFFF)) / params.alpha + params.minQuantile;
@@ -346,18 +349,19 @@ public final class OptimizedScalarQuantizationCodec {
         } else if (bits <= 32) {
             // int[] - treat as unsigned
             if (!(quantizedBytes instanceof int[])) {
-                throw new IllegalArgumentException(
-                        "Expected int[] for " + bits + " bits, got " + quantizedBytes.getClass().getName());
+                throw new RuntimeDataException(ErrorCode.ILLEGAL_STATE, "Expected an int[] quantized vector for " + bits
+                        + " bits, but got a " + quantizedBytes.getClass().getName());
             }
             int[] ints = (int[]) quantizedBytes;
             if (ints.length != dims) {
-                throw new IllegalArgumentException("Array length mismatch: expected " + dims + ", got " + ints.length);
+                throw new RuntimeDataException(ErrorCode.VECTOR_DIMENSION_MISMATCH, dims, ints.length);
             }
             for (int i = 0; i < dims; i++) {
                 result[i] = ((double) (ints[i] & 0xFFFFFFFFL)) / params.alpha + params.minQuantile;
             }
         } else {
-            throw new IllegalArgumentException("bits must be <= 32, got " + bits);
+            throw new RuntimeDataException(ErrorCode.ILLEGAL_STATE,
+                    "Unsupported quantization bit width " + bits + "; the maximum is 32");
         }
 
         return result;
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/vector/OptimizedScalarQuantizerFactory.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/vector/OptimizedScalarQuantizerFactory.java
index f8b97738d0..6013952362 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/vector/OptimizedScalarQuantizerFactory.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/vector/OptimizedScalarQuantizerFactory.java
@@ -18,6 +18,7 @@
  */
 package org.apache.asterix.common.vector;
 
+import org.apache.hyracks.api.exceptions.ErrorCode;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.storage.am.vector.api.IVTreeQuantizer;
 import org.apache.hyracks.storage.am.vector.api.IVTreeQuantizerFactory;
@@ -51,7 +52,8 @@ public class OptimizedScalarQuantizerFactory implements IVTreeQuantizerFactory {
     public IVTreeQuantizer createQuantizer(int vectorDimensions, VTreeQuantizationParams params)
             throws HyracksDataException {
         if (params == null) {
-            throw HyracksDataException.create(new IllegalArgumentException("Quantization params must not be null"));
+            throw HyracksDataException.create(ErrorCode.ILLEGAL_STATE,
+                    "Null quantization params reached the quantizer factory");
         }
 
         OptimizedScalarQuantizationCodec.Params p =
diff --git a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
index 5d349af163..0ff4d5a9e0 100644
--- a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
+++ b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
@@ -371,6 +371,8 @@
 1257 = ANN distance metric '%1$s' does not match vector index '%2$s' (similarity in metadata: '%3$s')
 1258 = Failed to create vector index. %1$s
 1259 = %1$s execution is not supported for a request with multiple statements
+1260 = Unsupported type %1$s in the vector value; the supported element types are numbers
+1261 = Vector dimension mismatch: expected %1$s dimensions, but got %2$s
 
 # Feed Errors
 3001 = Illegal state.
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/VectorIndexDeclUtil.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/VectorIndexDeclUtil.java
index 35fe62a212..cb2fcad22b 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/VectorIndexDeclUtil.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/VectorIndexDeclUtil.java
@@ -112,8 +112,8 @@ public class VectorIndexDeclUtil {
     private static void validateWithClauseFieldNames(AdmObjectNode node) throws CompilationException {
         for (String name : node.getFieldNames()) {
             if (!VectorIndexParameters.isKnown(name)) {
-                throw new CompilationException("Failed to create vector index. Unknown field `" + name
-                        + "` in WITH clause. Allowed fields: " + VectorIndexParameters.nameList());
+                throw new CompilationException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED, "Unknown field `"
+                        + name + "` in WITH clause. Allowed fields: " + VectorIndexParameters.nameList());
             }
         }
     }
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java
index 84909254e9..5b0832e186 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java
@@ -831,8 +831,8 @@ public class MetadataProvider implements IMetadataProvider<DataSourceId, String>
         Index vectorIndex = MetadataManager.INSTANCE.getIndex(mdTxnCtx, dataset.getDatabaseName(),
                 dataset.getDataverseName(), dataset.getDatasetName(), indexName);
         if (vectorIndex == null) {
-            throw new AlgebricksException(
-                    "Code generation error: no index " + indexName + " for dataset " + dataset.getDatasetName());
+            throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "no index " + indexName
+                    + " was found for collection " + dataset.getDatasetName() + " during code generation");
         }
 
         RecordDescriptor outputRecDesc = JobGenHelper.mkRecordDescriptor(typeEnv, opSchema, context);
@@ -1761,7 +1761,8 @@ public class MetadataProvider implements IMetadataProvider<DataSourceId, String>
 
             return new Pair<>(op, partitioningProperties.getConstraints());
         } catch (Exception e) {
-            throw new AlgebricksException(e);
+            throw CompilationException.create(ErrorCode.COMPILATION_ERROR, e,
+                    "failed to generate the vector index modification runtime: " + e.getMessage());
         }
     }
 
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/SampleOperationsHelper.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/SampleOperationsHelper.java
index ac5cd380e6..b365dba58a 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/SampleOperationsHelper.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/SampleOperationsHelper.java
@@ -27,6 +27,8 @@ import java.util.Set;
 
 import org.apache.asterix.common.cluster.PartitioningProperties;
 import org.apache.asterix.common.config.OptimizationConfUtil;
+import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.common.utils.StorageConstants;
 import org.apache.asterix.formats.base.IDataFormat;
 import org.apache.asterix.metadata.declared.MetadataProvider;
@@ -530,7 +532,9 @@ public class SampleOperationsHelper implements ISecondaryIndexOperationsHelper {
     }
 
     @Override
-    public JobSpecification buildStaticStructureJobSpec() {
-        throw new UnsupportedOperationException();
+    public JobSpecification buildStaticStructureJobSpec() throws AlgebricksException {
+        // Samples are BTrees; only vector indexes have a static-structure build phase.
+        throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE,
+                "a static-structure job was requested for the sample index");
     }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/AOrderedListVectorBinaryAccessor.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/AOrderedListVectorBinaryAccessor.java
index 3301b03291..6b4d4d3e3b 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/AOrderedListVectorBinaryAccessor.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/AOrderedListVectorBinaryAccessor.java
@@ -18,6 +18,8 @@
  */
 package org.apache.asterix.dataflow.data.common;
 
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.exceptions.RuntimeDataException;
 import org.apache.asterix.dataflow.data.nontagged.serde.AOrderedListSerializerDeserializer;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.EnumDeserializer;
@@ -53,6 +55,9 @@ public class AOrderedListVectorBinaryAccessor implements IVTreeBinaryAccessor {
     @Override
     public double[] getVector() throws HyracksDataException {
         if (data == null) {
+            // TODO(vector-errors): uncoded IllegalStateException -> surfaces to the user as "Internal error".
+            // Reachable only if a caller skips reset(); decide whether to keep it as an unchecked
+            // invariant or convert to RuntimeDataException(ErrorCode.ILLEGAL_STATE, ...).
             throw new IllegalStateException("Accessor not initialized. Call reset() first.");
         }
 
@@ -69,8 +74,12 @@ public class AOrderedListVectorBinaryAccessor implements IVTreeBinaryAccessor {
         return vector;
     }
 
+    @Override
     public int getDimension() throws HyracksDataException {
         if (data == null) {
+            // TODO(vector-errors): uncoded IllegalStateException -> surfaces to the user as "Internal error".
+            // Reachable only if a caller skips reset(); decide whether to keep it as an unchecked
+            // invariant or convert to RuntimeDataException(ErrorCode.ILLEGAL_STATE, ...).
             throw new IllegalStateException("Accessor not initialized. Call reset() first.");
         }
         return listLength;
@@ -99,17 +108,12 @@ public class AOrderedListVectorBinaryAccessor implements IVTreeBinaryAccessor {
             valueOffset = itemOffset + 1; // Skip type tag
         }
 
-        switch (actualType) {
-            case DOUBLE:
-                return DoublePointable.getDouble(data, valueOffset);
-            case FLOAT:
-                return FloatPointable.getFloat(data, valueOffset);
-            case INTEGER:
-                return IntegerPointable.getInteger(data, valueOffset);
-            case BIGINT:
-                return LongPointable.getLong(data, valueOffset);
-            default:
-                throw new HyracksDataException("Unsupported numeric type in vector: " + actualType);
-        }
+        return switch (actualType) {
+            case DOUBLE -> DoublePointable.getDouble(data, valueOffset);
+            case FLOAT -> FloatPointable.getFloat(data, valueOffset);
+            case INTEGER -> IntegerPointable.getInteger(data, valueOffset);
+            case BIGINT -> LongPointable.getLong(data, valueOffset);
+            default -> throw new RuntimeDataException(ErrorCode.UNSUPPORTED_VECTOR_ELEMENT_TYPE, actualType);
+        };
     }
 }
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/QuantizationConstantsAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/QuantizationConstantsAggregateDescriptor.java
index 9c644fc501..467fbdc66e 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/QuantizationConstantsAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/QuantizationConstantsAggregateDescriptor.java
@@ -24,6 +24,8 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.exceptions.RuntimeDataException;
 import org.apache.asterix.dataflow.data.nontagged.serde.ADoubleSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.AFloatSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.AInt16SerializerDeserializer;
@@ -176,51 +178,61 @@ public class QuantizationConstantsAggregateDescriptor extends AbstractAggregateF
             int contentLength = valuesPointable.getContentLength();
 
             if (contentLength < Integer.BYTES) {
-                throw new HyracksDataException("Binary data too short: expected at least " + Integer.BYTES
-                        + " bytes for numValues, got " + contentLength);
+                throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                        "Quantization constants are too short: expected at least " + Integer.BYTES
+                                + " bytes for numValues, got " + contentLength);
             }
 
             if (contentStartOffset + Integer.BYTES > valuesBytes.length) {
-                throw new HyracksDataException("Out of bounds: trying to read numValues at offset " + contentStartOffset
-                        + ", but array length is " + valuesBytes.length);
+                throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                        "Quantization constants are out of bounds: reading numValues at offset " + contentStartOffset
+                                + ", but the array length is " + valuesBytes.length);
             }
 
             int numValues = IntegerPointable.getInteger(valuesBytes, contentStartOffset);
 
             if (numValues < 0) {
-                throw new HyracksDataException("Invalid numValues: " + numValues + " (negative)");
+                throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                        "Quantization constants declare a negative value count: " + numValues);
             }
             if (numValues > 10000000) {
-                throw new HyracksDataException("Invalid numValues: " + numValues + " (too large, possible corruption)");
+                throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                        "Quantization constants declare an implausibly large value count: " + numValues);
             }
 
             long expectedBytes = (long) Integer.BYTES + ((long) numValues * (long) Double.BYTES);
             if (expectedBytes > Integer.MAX_VALUE) {
-                throw new HyracksDataException("Calculated expected bytes exceeds Integer.MAX_VALUE: " + expectedBytes);
+                throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                        "Quantization constants require " + expectedBytes + " bytes, which exceeds the maximum "
+                                + Integer.MAX_VALUE);
             }
             int expectedBytesInt = (int) expectedBytes;
 
             if (expectedBytesInt > contentLength) {
-                throw new HyracksDataException("Binary data too short: expected " + expectedBytesInt + " bytes, got "
-                        + contentLength + " (numValues=" + numValues + ")");
+                throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                        "Quantization constants are too short: expected " + expectedBytesInt + " bytes, got "
+                                + contentLength + " for " + numValues + " values");
             }
 
             int lastByteOffset = contentStartOffset + expectedBytesInt - 1;
             if (lastByteOffset >= valuesBytes.length) {
-                throw new HyracksDataException("Out of bounds: trying to read up to offset " + lastByteOffset
-                        + ", but array length is " + valuesBytes.length);
+                throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                        "Quantization constants are out of bounds: reading up to offset " + lastByteOffset
+                                + ", but the array length is " + valuesBytes.length);
             }
 
             int pointer = contentStartOffset + Integer.BYTES;
             for (int i = 0; i < numValues; i++) {
                 if (pointer + Double.BYTES > contentStartOffset + contentLength) {
-                    throw new HyracksDataException("Out of bounds: trying to read double #" + (i + 1) + " at offset "
-                            + pointer + ", but content ends at " + (contentStartOffset + contentLength));
+                    throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                            "Quantization constants are out of bounds: reading value #" + (i + 1) + " at offset "
+                                    + pointer + ", but the content ends at " + (contentStartOffset + contentLength));
                 }
 
                 if (pointer + Double.BYTES > valuesBytes.length) {
-                    throw new HyracksDataException("Out of bounds: trying to read double #" + (i + 1) + " at offset "
-                            + pointer + ", but array length is " + valuesBytes.length);
+                    throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                            "Quantization constants are out of bounds: reading value #" + (i + 1) + " at offset "
+                                    + pointer + ", but the array length is " + valuesBytes.length);
                 }
 
                 double value = BufferSerDeUtil.getDouble(valuesBytes, pointer);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor.java
index e632714117..db7398a2bc 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor.java
@@ -446,7 +446,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
 
                                 listAccessorConstant.reset(inputVal.getByteArray(), inputVal.getStartOffset());
                                 try {
-                                    double[] point = kMeansUtils.createPrimitveList(listAccessorConstant);
+                                    double[] point = kMeansUtils.createPrimitiveList(listAccessorConstant);
                                     if (!hasIndexDimension(point)) {
                                         tempIdx++;
                                         continue;
@@ -461,7 +461,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
                                     // Accumulate sum (NO STORAGE)
                                     totalDistance += minDist;
                                 } catch (IOException e) {
-                                    throw new RuntimeException(e);
+                                    throw HyracksDataException.create(e);
                                 }
                                 tempIdx++;
                             }
@@ -493,7 +493,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
 
                                 listAccessorConstant.reset(inputVal.getByteArray(), inputVal.getStartOffset());
                                 try {
-                                    double[] point = kMeansUtils.createPrimitveList(listAccessorConstant);
+                                    double[] point = kMeansUtils.createPrimitiveList(listAccessorConstant);
                                     if (!hasIndexDimension(point)) {
                                         currentIdx++;
                                         continue;
@@ -516,7 +516,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
                                         sampledCount++;
                                     }
                                 } catch (IOException e) {
-                                    throw new RuntimeException(e);
+                                    throw HyracksDataException.create(e);
                                 }
                                 currentIdx++;
                             }
@@ -555,7 +555,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
 
                             listAccessorConstant.reset(inputVal.getByteArray(), inputVal.getStartOffset());
                             try {
-                                double[] point = kMeansUtils.createPrimitveList(listAccessorConstant);
+                                double[] point = kMeansUtils.createPrimitiveList(listAccessorConstant);
                                 if (!hasIndexDimension(point)) {
                                     weightIdx++;
                                     continue;
@@ -575,7 +575,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
                                     candidateWeights[nearestCandidate]++;
                                 }
                             } catch (IOException e) {
-                                throw new RuntimeException(e);
+                                throw HyracksDataException.create(e);
                             }
                             weightIdx++;
                         }
@@ -710,7 +710,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
 
                                 listAccessorConstant.reset(inputVal.getByteArray(), inputVal.getStartOffset());
                                 try {
-                                    double[] point = kMeansUtils.createPrimitveList(listAccessorConstant);
+                                    double[] point = kMeansUtils.createPrimitiveList(listAccessorConstant);
                                     if (!hasIndexDimension(point)) {
                                         currentIdx++;
                                         continue;
@@ -729,7 +729,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
                                     assignments[currentIdx] = closestCentroid;
 
                                 } catch (IOException e) {
-                                    throw new RuntimeException(e);
+                                    throw HyracksDataException.create(e);
                                 }
                                 currentIdx++;
                             }
@@ -760,7 +760,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
 
                                 listAccessorConstant.reset(inputVal.getByteArray(), inputVal.getStartOffset());
                                 try {
-                                    double[] point = kMeansUtils.createPrimitveList(listAccessorConstant);
+                                    double[] point = kMeansUtils.createPrimitiveList(listAccessorConstant);
                                     if (!hasIndexDimension(point)) {
                                         currentIdx++;
                                         continue;
@@ -773,7 +773,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
                                     counts[centroidIdx]++;
 
                                 } catch (IOException e) {
-                                    throw new RuntimeException(e);
+                                    throw HyracksDataException.create(e);
                                 }
                                 currentIdx++;
                             }
@@ -1068,7 +1068,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
                                 continue;
                             }
                             listAccessor.reset(inputVal.getByteArray(), inputVal.getStartOffset());
-                            double[] point = kMeansUtils.createPrimitveList(listAccessor);
+                            double[] point = kMeansUtils.createPrimitiveList(listAccessor);
                             if (hasIndexDimension(point)) {
                                 return point;
                             }
@@ -1103,7 +1103,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
                                 }
 
                                 listAccessorConstant.reset(inputVal.getByteArray(), inputVal.getStartOffset());
-                                return kMeansUtils.createPrimitveList(listAccessorConstant);
+                                return kMeansUtils.createPrimitiveList(listAccessorConstant);
                             }
                             currentIndex++;
                         }
@@ -1141,7 +1141,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
                                 if (ATYPETAGDESERIALIZER.deserialize(inputVal.getByteArray()[inputVal.getStartOffset()])
                                         .isListType()) {
                                     listAccessorConstant.reset(inputVal.getByteArray(), inputVal.getStartOffset());
-                                    results.set(ti, kMeansUtils.createPrimitveList(listAccessorConstant));
+                                    results.set(ti, kMeansUtils.createPrimitiveList(listAccessorConstant));
                                 }
                                 ti++;
                             }
@@ -1462,11 +1462,7 @@ public final class HierarchicalKMeansPlusPlusCentroidsOperatorDescriptor extends
                 }
 
                 private static IVTreeDistanceFunction distanceFunctionFor(VectorSimilarityMetric metric) {
-                    try {
-                        return new VectorDistanceFunctionFactory(metric).createDistanceFunction();
-                    } catch (HyracksDataException e) {
-                        throw new IllegalStateException("Failed to build vector distance function for " + metric, e);
-                    }
+                    return new VectorDistanceFunctionFactory(metric).createDistanceFunction();
                 }
             };
         }
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/KMeansUtils.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/KMeansUtils.java
index e47350f7fb..01a9d10c99 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/KMeansUtils.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/KMeansUtils.java
@@ -20,6 +20,8 @@ package org.apache.asterix.runtime.operators;
 
 import java.io.IOException;
 
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.exceptions.RuntimeDataException;
 import org.apache.asterix.dataflow.data.nontagged.serde.ADoubleSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.AFloatSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.AInt16SerializerDeserializer;
@@ -46,7 +48,8 @@ public class KMeansUtils {
     }
 
     // Euclidean Distance
-    protected double[] createPrimitveList(ListAccessor listAccessor) throws IOException {
+    protected double[] createPrimitiveList(ListAccessor listAccessor) throws IOException {
+        // TODO(vector-errors): update consumers to create HyracksDataException.create(e);
         ATypeTag typeTag = listAccessor.getItemType();
         double[] primitiveArray = new double[listAccessor.size()];
         storage.reset();
@@ -66,11 +69,12 @@ public class KMeansUtils {
             ATypeTag typeTag = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(data[offset]);
             return getValueFromTag(typeTag, data, offset);
         } else {
-            throw new HyracksDataException("Unsupported type tag for numeric vector extraction: " + derivedTypeTag);
+            throw new RuntimeDataException(ErrorCode.UNSUPPORTED_VECTOR_ELEMENT_TYPE, derivedTypeTag);
         }
     }
 
     protected double getValueFromTag(ATypeTag typeTag, byte[] data, int offset) throws HyracksDataException {
+        // TODO(vector-index): refactor
         return switch (typeTag) {
             case TINYINT -> AInt8SerializerDeserializer.getByte(data, offset + 1);
             case SMALLINT -> AInt16SerializerDeserializer.getShort(data, offset + 1);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/VTreeBulkLoaderAndGroupingOperatorDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/VTreeBulkLoaderAndGroupingOperatorDescriptor.java
index e0e933c961..d63558b369 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/VTreeBulkLoaderAndGroupingOperatorDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/VTreeBulkLoaderAndGroupingOperatorDescriptor.java
@@ -18,6 +18,8 @@
  */
 package org.apache.asterix.runtime.operators;
 
+import static org.apache.hyracks.api.exceptions.ErrorCode.ILLEGAL_STATE;
+
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.List;
@@ -126,8 +128,8 @@ public class VTreeBulkLoaderAndGroupingOperatorDescriptor extends AbstractSingle
         // validation, IndexTupleTranslator at persist), so a missing/unsupported value here means a corrupt index.
         VectorSimilarityMetric metric = (distanceType == null) ? null : VectorSimilarityMetric.fromAlias(distanceType);
         if (metric == null) {
-            throw new IllegalArgumentException(
-                    "Vector index has a missing or unsupported 'similarity' metric: " + distanceType);
+            throw HyracksDataException.create(ILLEGAL_STATE,
+                    "The index has a missing or unsupported 'similarity' metric: " + distanceType);
         }
         return new VectorDistanceFunctionFactory(metric).createDistanceFunction();
     }
@@ -339,7 +341,8 @@ public class VTreeBulkLoaderAndGroupingOperatorDescriptor extends AbstractSingle
         private double[] extractEmbedding(ITupleReference tuple) throws HyracksDataException {
             try {
                 if (tuple == null) {
-                    throw new IllegalArgumentException("Tuple cannot be null");
+                    throw HyracksDataException.create(ILLEGAL_STATE,
+                            "A null tuple reached the vector index bulk loader");
                 }
                 embeddingEval.evaluate((IFrameTupleReference) tuple, embeddingInputVal);
                 if (embeddingInputVal.getLength() == 0) {
@@ -351,7 +354,7 @@ public class VTreeBulkLoaderAndGroupingOperatorDescriptor extends AbstractSingle
                     return null;
                 }
                 embeddingListAccessor.reset(embeddingInputVal.getByteArray(), embeddingInputVal.getStartOffset());
-                double[] embedding = embeddingKMeansUtils.createPrimitveList(embeddingListAccessor);
+                double[] embedding = embeddingKMeansUtils.createPrimitiveList(embeddingListAccessor);
                 if (embedding == null || embedding.length == 0) {
                     return null;
                 }
@@ -395,14 +398,15 @@ public class VTreeBulkLoaderAndGroupingOperatorDescriptor extends AbstractSingle
                 IIndex indexInstance = indexHelper.getIndexInstance();
 
                 if (!(indexInstance instanceof ILSMIndex)) {
-                    throw new HyracksDataException("Index is not an ILSMIndex instance, got: "
-                            + (indexInstance != null ? indexInstance.getClass().getName() : "null"));
+                    throw HyracksDataException.create(ILLEGAL_STATE,
+                            "The vector index resource is not an " + "ILSMIndex, but a "
+                                    + (indexInstance != null ? indexInstance.getClass().getName() : "null"));
                 }
                 ILSMIndex lsmIndex = (ILSMIndex) indexInstance;
 
                 if (!(lsmIndex instanceof LSMVTree)) {
-                    throw new HyracksDataException(
-                            "Index is not an LSMVTree instance, got: " + lsmIndex.getClass().getName());
+                    throw HyracksDataException.create(ILLEGAL_STATE,
+                            "The vector index resource is not an LSMVTree, but a " + lsmIndex.getClass().getName());
                 }
                 LSMVTree = (LSMVTree) lsmIndex;
 
@@ -441,6 +445,9 @@ public class VTreeBulkLoaderAndGroupingOperatorDescriptor extends AbstractSingle
             // Get LocalResource from index helper (a genuine read failure propagates rather than defaulting)
             LocalResource localResource = indexHelper.getResource();
             if (localResource == null) {
+                // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
+                // A quantized vector index with no LocalResource is a broken invariant; decide whether to
+                // keep it unchecked or code it as ErrorCode.ILLEGAL_STATE / CORRUPTED_VECTOR_INDEX.
                 throw HyracksDataException.create(new IllegalStateException(
                         "Quantized VTree is missing its LocalResource; cannot read quantization params"));
             }
@@ -459,12 +466,14 @@ public class VTreeBulkLoaderAndGroupingOperatorDescriptor extends AbstractSingle
             }
 
             if (vcResource == null) {
+                // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
                 throw HyracksDataException
                         .create(new IllegalStateException("Quantized VTree resource is not an LSMVTreeLocalResource: "
                                 + (resource == null ? "null" : resource.getClass().getName())));
             }
 
             if (!vcResource.hasQuantizationParams()) {
+                // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
                 throw HyracksDataException
                         .create(new IllegalStateException("Quantized VTree resource is missing quantization params"));
             }
@@ -506,7 +515,7 @@ public class VTreeBulkLoaderAndGroupingOperatorDescriptor extends AbstractSingle
          * @return QuantizedVector containing per-dimension quantized bytes (and metadata)
          */
         private OptimizedScalarQuantizationCodec.QuantizedVector quantizeVector(double[] embedding,
-                OptimizedScalarQuantizationCodec.Params params, String distanceMetric) {
+                OptimizedScalarQuantizationCodec.Params params, String distanceMetric) throws HyracksDataException {
             if (embedding == null || params == null) {
                 return null;
             }
@@ -521,18 +530,22 @@ public class VTreeBulkLoaderAndGroupingOperatorDescriptor extends AbstractSingle
                 throws HyracksDataException {
             try {
                 if (queryVector == null) {
-                    throw new IllegalArgumentException("Query vector cannot be null");
+                    throw HyracksDataException.create(ILLEGAL_STATE,
+                            "A null query vector reached the vector index centroid search");
                 }
 
                 if (queryVector.length == 0) {
-                    throw new IllegalArgumentException("Query vector cannot be empty");
+                    throw HyracksDataException.create(ILLEGAL_STATE,
+                            "An empty query vector reached the vector index centroid search");
                 }
 
                 if (vcTreeAccessor == null) {
+                    // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
                     throw new IllegalStateException("VTreeAccessor not initialized");
                 }
 
                 if (distanceFunction == null) {
+                    // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
                     throw new IllegalStateException("DistanceFunction not initialized");
                 }
 
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/VTreeStaticStructureCreatorOperatorDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/VTreeStaticStructureCreatorOperatorDescriptor.java
index 098d3c80fe..f4ef006539 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/VTreeStaticStructureCreatorOperatorDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/VTreeStaticStructureCreatorOperatorDescriptor.java
@@ -18,6 +18,8 @@
  */
 package org.apache.asterix.runtime.operators;
 
+import static org.apache.hyracks.api.exceptions.ErrorCode.ILLEGAL_STATE;
+
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -25,6 +27,8 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.asterix.common.dataflow.DatasetLocalResource;
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.exceptions.RuntimeDataException;
 import org.apache.asterix.common.ioopcallbacks.LSMIOOperationCallback;
 import org.apache.asterix.common.vector.OptimizedScalarQuantizationCodec;
 import org.apache.asterix.om.types.ATypeTag;
@@ -327,7 +331,8 @@ public class VTreeStaticStructureCreatorOperatorDescriptor extends AbstractOpera
                         }
 
                         if (embedding == null || embedding.length == 0) {
-                            throw new HyracksDataException("Failed to extract embedding from hierarchical tuple");
+                            throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                                    "Failed to extract an embedding from a centroid tuple");
                         }
 
                         // Check if this is a leaf level (maxLevel) - apply quantization only to leaf nodes
@@ -342,7 +347,8 @@ public class VTreeStaticStructureCreatorOperatorDescriptor extends AbstractOpera
                         }
 
                     } catch (Exception e) {
-                        throw new HyracksDataException("Failed to convert tuple to VCTreeBuilder format", e);
+                        throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED, e,
+                                "Failed to convert a centroid tuple to the static-structure builder format");
                     }
                 }
 
@@ -411,8 +417,9 @@ public class VTreeStaticStructureCreatorOperatorDescriptor extends AbstractOpera
                                 quantizedBytes[i * 4 + 3] = (byte) ints[i];
                             }
                         } else {
-                            throw new HyracksDataException(
-                                    "Unexpected quantized bytes type: " + quantizedResult.quantizedBytes.getClass());
+                            throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                                    "Unexpected quantized-bytes type "
+                                            + quantizedResult.quantizedBytes.getClass().getName());
                         }
 
                         // Create 4-field tuple: [centroidId, embedding, quantizedBytes, neighborList].
@@ -467,10 +474,10 @@ public class VTreeStaticStructureCreatorOperatorDescriptor extends AbstractOpera
                     List<List<Integer>> centroidsPerCluster = new ArrayList<>();
 
                     if (levelDistribution == null || levelDistribution.isEmpty()) {
-                        throw HyracksDataException.create(
-                                new RuntimeException("Cannot build vector index: no training vectors were found. "
-                                        + "Verify that the indexed field exists in the dataset and contains vector data, "
-                                        + "and that sampling produced enough vectors for train_list_fraction."));
+                        throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                                "No training vectors were found. Verify that the indexed field exists in the "
+                                        + "collection and contains vector data, and that sampling produced enough "
+                                        + "vectors for train_list_fraction");
                     }
 
                     // Find max level
@@ -571,14 +578,16 @@ public class VTreeStaticStructureCreatorOperatorDescriptor extends AbstractOpera
 
                         IIndex indexInstance = partitionHelper.getIndexInstance();
                         if (!(indexInstance instanceof ILSMIndex)) {
-                            throw new HyracksDataException("Index is not an ILSMIndex instance, got: "
-                                    + (indexInstance != null ? indexInstance.getClass().getName() : "null"));
+                            throw HyracksDataException.create(ILLEGAL_STATE,
+                                    "The vector index resource is not an ILSMIndex, but a "
+                                            + (indexInstance != null ? indexInstance.getClass().getName() : "null"));
                         }
                         ILSMIndex partitionLsmIndex = (ILSMIndex) indexInstance;
 
                         if (!(partitionLsmIndex instanceof LSMVTree)) {
-                            throw new HyracksDataException("Index is not an LSMVTree instance, got: "
-                                    + partitionLsmIndex.getClass().getName());
+                            throw HyracksDataException.create(ILLEGAL_STATE,
+                                    "The vector index resource is not an LSMVTree, but a "
+                                            + partitionLsmIndex.getClass().getName());
                         }
 
                         Map<String, Object> parameters = new HashMap<>();
@@ -678,7 +687,7 @@ public class VTreeStaticStructureCreatorOperatorDescriptor extends AbstractOpera
             case BIGINT:
                 return LongPointable.getLong(data, start + 1);
             default:
-                throw new HyracksDataException("Unsupported numeric type: " + typeTag);
+                throw new RuntimeDataException(ErrorCode.UNSUPPORTED_VECTOR_ELEMENT_TYPE, typeTag);
         }
     }
 
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/VectorComponentExtractorOperatorDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/VectorComponentExtractorOperatorDescriptor.java
index 0fd68f1037..9c26424e3a 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/VectorComponentExtractorOperatorDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/operators/VectorComponentExtractorOperatorDescriptor.java
@@ -200,7 +200,8 @@ public class VectorComponentExtractorOperatorDescriptor extends AbstractSingleAc
                             appender.reset(new VSizeFrame(ctx), true);
                             if (!appender.append(tupleBuilder.getFieldEndOffsets(), tupleBuilder.getByteArray(), 0,
                                     tupleBuilder.getSize())) {
-                                throw HyracksDataException.create(new IOException("Tuple too large to fit in frame"));
+                                throw new RuntimeDataException(ErrorCode.COMPILATION_VECTOR_INDEX_CREATION_FAILED,
+                                        "A vector is too large to fit in a single frame");
                             }
                         }
                     } catch (IOException e) {
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/utils/VectorDistanceCalculation.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/utils/VectorDistanceCalculation.java
index b7d7f9e817..59c0367d67 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/utils/VectorDistanceCalculation.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/utils/VectorDistanceCalculation.java
@@ -19,6 +19,8 @@
 
 package org.apache.asterix.runtime.utils;
 
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.exceptions.RuntimeDataException;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.data.std.primitive.DoublePointable;
 import org.apache.hyracks.dataflow.common.data.marshalling.DoubleArraySerializerDeserializer;
@@ -244,8 +246,7 @@ public class VectorDistanceCalculation {
     private static int checkedLength(byte[] bytes, int offset, int length, double[] dst) throws HyracksDataException {
         int len = DoubleArraySerializerDeserializer.readLength(bytes, offset, length);
         if (dst != null && dst.length != len) {
-            throw HyracksDataException.create(new IllegalArgumentException(
-                    "destination has " + dst.length + " elements but the encoded array has " + len));
+            throw new RuntimeDataException(ErrorCode.VECTOR_DIMENSION_MISMATCH, dst.length, len);
         }
         return len;
     }
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/utils/VectorDistanceFunctionFactory.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/utils/VectorDistanceFunctionFactory.java
index 429fb11a81..72c43de850 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/utils/VectorDistanceFunctionFactory.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/utils/VectorDistanceFunctionFactory.java
@@ -63,7 +63,7 @@ public class VectorDistanceFunctionFactory implements IVTreeDistanceFunctionFact
     }
 
     @Override
-    public IVTreeDistanceFunction createDistanceFunction() throws HyracksDataException {
+    public IVTreeDistanceFunction createDistanceFunction() {
         return functionFor(metric);
     }
 
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/ErrorCode.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/ErrorCode.java
index d83270f118..fd9bf738c4 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/ErrorCode.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/ErrorCode.java
@@ -163,6 +163,9 @@ public enum ErrorCode implements IError {
     EMPTY_TYPE_INFERRED(133),
     SCHEMA_LIMIT_EXCEEDED(134),
     FAILED_IO_OPERATION(135),
+    VECTOR_INDEX_BUILD_FAILED(136),
+    MALFORMED_VECTOR_INDEX(137),
+    UNEXPECTED_VECTOR_VALUE(138),
 
     // Compilation error codes.
     RULECOLLECTION_NOT_INSTANCE_OF_LIST(10000),
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/resources/errormsg/en.properties b/hyracks-fullstack/hyracks/hyracks-api/src/main/resources/errormsg/en.properties
index 8e16262c78..a8e0de33a9 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/resources/errormsg/en.properties
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/resources/errormsg/en.properties
@@ -153,6 +153,9 @@
 133 = Schema could not be inferred, empty types found in the result
 134 = Schema Limit exceeded, maximum number of heterogeneous schemas allowed : '%1$s'
 135 = An IO Operation has failed
+136 = Failed to build the vector index. %1$s
+137 = Malformed vector index. %1$s
+138 = Encountered unexpected vector. %1$s
 
 10000 = The given rule collection %1$s is not an instance of the List class.
 10001 = Cannot compose partition constraint %1$s with %2$s
diff --git a/hyracks-fullstack/hyracks/hyracks-dataflow-common/src/main/java/org/apache/hyracks/dataflow/common/data/marshalling/DoubleArraySerializerDeserializer.java b/hyracks-fullstack/hyracks/hyracks-dataflow-common/src/main/java/org/apache/hyracks/dataflow/common/data/marshalling/DoubleArraySerializerDeserializer.java
index 90acfde418..58430f0e56 100644
--- a/hyracks-fullstack/hyracks/hyracks-dataflow-common/src/main/java/org/apache/hyracks/dataflow/common/data/marshalling/DoubleArraySerializerDeserializer.java
+++ b/hyracks-fullstack/hyracks/hyracks-dataflow-common/src/main/java/org/apache/hyracks/dataflow/common/data/marshalling/DoubleArraySerializerDeserializer.java
@@ -20,10 +20,10 @@ package org.apache.hyracks.dataflow.common.data.marshalling;
 
 import java.io.DataInput;
 import java.io.DataOutput;
-import java.io.EOFException;
 import java.io.IOException;
 
 import org.apache.hyracks.api.dataflow.value.ISerializerDeserializer;
+import org.apache.hyracks.api.exceptions.ErrorCode;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.data.std.primitive.DoublePointable;
 import org.apache.hyracks.data.std.primitive.IntegerPointable;
@@ -96,14 +96,14 @@ public class DoubleArraySerializerDeserializer implements ISerializerDeserialize
      */
     public static int readLength(byte[] bytes, int offset, int length) throws HyracksDataException {
         if (length < Integer.BYTES) {
-            throw HyracksDataException.create(new EOFException(
-                    "array field is " + length + " bytes, need at least " + Integer.BYTES + " for the length prefix"));
+            throw HyracksDataException.create(ErrorCode.MALFORMED_VECTOR_INDEX, "The encoded array field is " + length
+                    + " bytes, but at least " + Integer.BYTES + " are needed for the length prefix");
         }
         int len = IntegerPointable.getInteger(bytes, offset);
         long required = (long) Integer.BYTES + (long) len * Double.BYTES;
         if (len < 0 || required > length) {
-            throw HyracksDataException.create(new EOFException(
-                    "encoded array length " + len + " needs " + required + " bytes but the field is " + length));
+            throw HyracksDataException.create(ErrorCode.MALFORMED_VECTOR_INDEX, "The encoded array declares " + len
+                    + " elements, needing " + required + " bytes, but the field is " + length + " bytes");
         }
         return len;
     }
@@ -117,8 +117,8 @@ public class DoubleArraySerializerDeserializer implements ISerializerDeserialize
     public static void readInto(byte[] bytes, int offset, int length, double[] dst) throws HyracksDataException {
         int len = readLength(bytes, offset, length);
         if (dst.length != len) {
-            throw HyracksDataException.create(new IllegalArgumentException(
-                    "destination has " + dst.length + " elements but the encoded array has " + len));
+            throw HyracksDataException.create(ErrorCode.UNEXPECTED_VECTOR_VALUE,
+                    "Expected to see " + dst.length + " elements but the encoded vector has " + len);
         }
         decode(bytes, offset, dst, len);
     }
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/dataflow/LSMVTreeLocalResource.java b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/dataflow/LSMVTreeLocalResource.java
index e6f928702a..2c891aba65 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/dataflow/LSMVTreeLocalResource.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/dataflow/LSMVTreeLocalResource.java
@@ -186,14 +186,14 @@ public class LSMVTreeLocalResource extends LsmResource implements IQuantizedReso
         // tripped through the resource registry on JSON read. A null here indicates a corrupted
         // resource (e.g., a JSON file with the {@link #KEY_VECTOR_ACCESSOR_FACTORY} key missing).
         if (vectorAccessorFactory == null) {
-            throw new HyracksDataException("Vector accessor factory missing from LSMVTreeLocalResource (path=" + path
-                    + "); resource is corrupted or was written by an unsupported codebase version.");
+            throw HyracksDataException.create(ErrorCode.ILLEGAL_STATE,
+                    "The vector accessor factory is missing from the resource at " + path);
         }
         // Distance-function factory is always supplied at DDL and round-tripped through the registry;
         // a null indicates a corrupted or unsupported-version resource.
         if (distanceFunctionFactory == null) {
-            throw new HyracksDataException("Distance-function factory missing from LSMVTreeLocalResource (path=" + path
-                    + "); resource is corrupted or was written by an unsupported codebase version.");
+            throw HyracksDataException.create(ErrorCode.ILLEGAL_STATE,
+                    "The distance-function factory is missing from the resource at " + path);
         }
 
         // Quantization params for lazy quantizer creation at query time
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/dataflow/QuantizedIndexCreateOperatorDescriptor.java b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/dataflow/QuantizedIndexCreateOperatorDescriptor.java
index 54d77a32aa..fbecb7caea 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/dataflow/QuantizedIndexCreateOperatorDescriptor.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/dataflow/QuantizedIndexCreateOperatorDescriptor.java
@@ -179,7 +179,8 @@ public class QuantizedIndexCreateOperatorDescriptor extends AbstractSingleActivi
                     indexBuilder.build();
                 }
             } else {
-                throw new HyracksDataException("QuantizedIndexCreateOperator: No quantization constants received.");
+                throw HyracksDataException.create(ErrorCode.VECTOR_INDEX_BUILD_FAILED,
+                        "No quantization constants were received");
             }
         }
     }
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/dataflow/VTreeSearchOperatorNodePushable.java b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/dataflow/VTreeSearchOperatorNodePushable.java
index 849a869a16..f67733dd1c 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/dataflow/VTreeSearchOperatorNodePushable.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/dataflow/VTreeSearchOperatorNodePushable.java
@@ -265,6 +265,9 @@ public class VTreeSearchOperatorNodePushable extends IndexSearchOperatorNodePush
                 value = LongPointable.getLong(data, valueOffset);
                 break;
             default:
+                // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
+                // The compiler types the query-parameters tuple, so a non-integer tag here is a plan bug
+                // rather than bad user input; confirm before coding it.
                 throw new IllegalStateException("Unexpected type tag " + typeTag + " for integer query parameter at "
                         + "position " + fieldIndex + " of the vector-search query-parameters tuple");
         }
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTree.java b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTree.java
index ac55787f31..f35746f147 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTree.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTree.java
@@ -252,6 +252,9 @@ public class LSMVTree extends AbstractLSMIndex implements ITreeIndex {
 
     public LSMVTreeDiskComponent getStaticStructure() {
         if (staticStructure == null) {
+            // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
+            // getStaticStructure() has no throws clause, so coding this needs a signature change; decide
+            // whether that is worth it or whether the invariant should stay unchecked.
             throw new IllegalStateException("Static structure must be built before loading records");
         }
         return staticStructure;
@@ -375,9 +378,10 @@ public class LSMVTree extends AbstractLSMIndex implements ITreeIndex {
     }
 
     @Override
-    public void scanDiskComponents(ILSMIndexOperationContext ictx, IIndexCursor cursor) {
+    public void scanDiskComponents(ILSMIndexOperationContext ictx, IIndexCursor cursor) throws HyracksDataException {
         // Vector clustering trees don't support disk-component scanning the way BTrees do.
-        throw new UnsupportedOperationException("Disk component scanning not supported for vector clustering trees");
+        throw HyracksDataException.create(ErrorCode.INVALID_OPERATOR_OPERATION, "scanDiskComponents",
+                LSMVTree.class.getSimpleName());
     }
 
     @Override
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTreeDiskComponent.java b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTreeDiskComponent.java
index 9cc0d83493..0799c3b927 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTreeDiskComponent.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTreeDiskComponent.java
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.hyracks.api.exceptions.ErrorCode;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.control.common.controllers.NCConfig;
 import org.apache.hyracks.storage.am.common.api.IMetadataPageManager;
@@ -178,9 +179,10 @@ public class LSMVTreeDiskComponent extends AbstractLSMDiskComponent {
         Integer maxEntriesParam = (Integer) parameters.get(PARAM_MAX_ENTRIES_PER_PAGE);
         if (numLevelsParam == null || clustersPerLevel == null || centroidsPerCluster == null
                 || maxEntriesParam == null) {
-            throw new HyracksDataException("static-structure bulk load requires non-null " + PARAM_NUM_LEVELS + ", "
-                    + PARAM_CLUSTERS_PER_LEVEL + ", " + PARAM_CENTROIDS_PER_CLUSTER + ", and "
-                    + PARAM_MAX_ENTRIES_PER_PAGE + " parameters");
+            throw HyracksDataException.create(ErrorCode.ILLEGAL_STATE,
+                    "A static-structure bulk load requires non-null " + PARAM_NUM_LEVELS + ", "
+                            + PARAM_CLUSTERS_PER_LEVEL + ", " + PARAM_CENTROIDS_PER_CLUSTER + " and "
+                            + PARAM_MAX_ENTRIES_PER_PAGE + " parameters");
         }
         int numLevels = numLevelsParam;
         int maxEntriesPerPage = maxEntriesParam;
@@ -198,8 +200,8 @@ public class LSMVTreeDiskComponent extends AbstractLSMDiskComponent {
     private IChainedComponentBulkLoader createVTreeBulkLoader(ILSMIOOperation operation) throws HyracksDataException {
         Object staticParam = operation.getParameters().get(PARAM_STATIC_STRUCTURE_COMPONENT);
         if (!(staticParam instanceof LSMVTreeDiskComponent staticComponent)) {
-            throw new HyracksDataException(
-                    PARAM_STATIC_STRUCTURE_COMPONENT + " must be provided in parameters for data loading");
+            throw HyracksDataException.create(ErrorCode.ILLEGAL_STATE,
+                    PARAM_STATIC_STRUCTURE_COMPONENT + " must be provided in the parameters for data loading");
         }
         ITreeIndexAccessor staticAccessor =
                 staticComponent.getIndex().createAccessor(NoOpIndexAccessParameters.INSTANCE);
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTreeSearchCursor.java b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTreeSearchCursor.java
index 77598be05c..023b53ba60 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTreeSearchCursor.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTreeSearchCursor.java
@@ -746,6 +746,9 @@ public class LSMVTreeSearchCursor extends LSMIndexSearchCursor {
                 // Merge ordering shares the exact <distance, PK> key with antimatter cancellation.
                 result = compareKey(cmp, tupleA, tupleB);
             } catch (Throwable e) {
+                // Matches the LSMIndexSearchCursor / LSMRTree / LSMBTree comparator idiom: Comparator.compare
+                // cannot throw a checked exception. The cause is a coded HyracksDataException and
+                // ExceptionUtils.unwrap() follows it, so the error code survives to the user.
                 throw new IllegalArgumentException(e);
             }
             if (result != 0) {
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTreeTopKSearchCursor.java b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTreeTopKSearchCursor.java
index 1a51cc7e0e..2ad7b52f4e 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTreeTopKSearchCursor.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/LSMVTreeTopKSearchCursor.java
@@ -274,8 +274,8 @@ public class LSMVTreeTopKSearchCursor extends EnforcedIndexCursor implements IVe
             this.quantizer = firstSearchCursor.getQuantizer();
 
             if (this.queryVector == null) {
-                throw HyracksDataException
-                        .create(new IllegalArgumentException("Query vector must be provided for naive blocked search"));
+                throw HyracksDataException.create(ErrorCode.ILLEGAL_STATE,
+                        "A query vector is required for the vector index blocked search");
             }
 
             // computeApproximateDistance() dequantizes every candidate with these two, and they arrive
@@ -700,6 +700,7 @@ public class LSMVTreeTopKSearchCursor extends EnforcedIndexCursor implements IVe
     @Override
     protected void doNext() throws HyracksDataException {
         if (!doHasNext()) {
+            // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
             throw HyracksDataException.create(new IllegalStateException("No more tuples"));
         }
         drainIterator.next();
@@ -824,6 +825,9 @@ public class LSMVTreeTopKSearchCursor extends EnforcedIndexCursor implements IVe
                     }
                 }
             } catch (Throwable e) {
+                // Matches the LSMIndexSearchCursor / LSMRTree / LSMBTree comparator idiom: Comparator.compare
+                // cannot throw a checked exception. The cause is a coded HyracksDataException and
+                // ExceptionUtils.unwrap() follows it, so the error code survives to the user.
                 throw new IllegalArgumentException(e);
             }
 
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/PKOnlyTupleProjector.java b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/PKOnlyTupleProjector.java
index 3bff0f09ca..e58ae8b94b 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/PKOnlyTupleProjector.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-vtree/src/main/java/org/apache/hyracks/storage/am/lsm/vector/impls/PKOnlyTupleProjector.java
@@ -21,6 +21,8 @@ package org.apache.hyracks.storage.am.lsm.vector.impls;
 import java.io.DataOutput;
 import java.io.IOException;
 
+import org.apache.hyracks.api.exceptions.ErrorCode;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.dataflow.common.comm.io.ArrayTupleBuilder;
 import org.apache.hyracks.dataflow.common.data.accessors.ITupleReference;
 import org.apache.hyracks.storage.common.projection.ITupleProjector;
@@ -57,8 +59,8 @@ public class PKOnlyTupleProjector implements ITupleProjector {
         int endField = numSecondaryKeys + numPrimaryKeys;
 
         if (endField > totalFields) {
-            throw new IOException("Invalid field range: trying to extract fields [" + startField + ", " + endField
-                    + ") from tuple with " + totalFields + " fields");
+            throw HyracksDataException.create(ErrorCode.ILLEGAL_STATE, "The primary-key projection needs fields ["
+                    + startField + ", " + endField + ") of a tuple that only has " + totalFields + " fields");
         }
 
         for (int i = startField; i < endField; i++) {
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/api/IVTreeDistanceFunctionFactory.java b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/api/IVTreeDistanceFunctionFactory.java
index 12229bb572..b37e8e071c 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/api/IVTreeDistanceFunctionFactory.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/api/IVTreeDistanceFunctionFactory.java
@@ -20,7 +20,6 @@ package org.apache.hyracks.storage.am.vector.api;
 
 import java.io.Serializable;
 
-import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.api.io.IJsonSerializable;
 
 /**
@@ -42,5 +41,5 @@ public interface IVTreeDistanceFunctionFactory extends Serializable, IJsonSerial
      * index creation (it is determined by the embedding model) and baked into the concrete factory, so
      * Hyracks never passes or is aware of a metric string. Must not return {@code null}.
      */
-    IVTreeDistanceFunction createDistanceFunction() throws HyracksDataException;
+    IVTreeDistanceFunction createDistanceFunction();
 }
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/frames/VTreeNSMFrame.java b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/frames/VTreeNSMFrame.java
index 6574156cb4..bdec9eba50 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/frames/VTreeNSMFrame.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/frames/VTreeNSMFrame.java
@@ -19,6 +19,7 @@
 
 package org.apache.hyracks.storage.am.vector.frames;
 
+import org.apache.hyracks.api.exceptions.ErrorCode;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.dataflow.common.data.accessors.ITupleReference;
 import org.apache.hyracks.storage.am.common.api.ISlotManager;
@@ -127,7 +128,8 @@ public abstract class VTreeNSMFrame extends TreeIndexNSMFrame implements IVTreeF
             IExtraPageBlockHelper extraPageBlockHelper, IBufferCache bufferCache) throws HyracksDataException {
         // Generic split is not supported on VTree frames; subclasses that split (e.g. data/metadata frames)
         // expose their own split(...) entry points with the right argument types.
-        throw new HyracksDataException("Split operation not implemented for " + this.getClass().getSimpleName());
+        throw HyracksDataException.create(ErrorCode.INVALID_OPERATOR_OPERATION, "split",
+                this.getClass().getSimpleName());
     }
 
     @Override
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTree.java b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTree.java
index 2a779e3294..0891328f1f 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTree.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTree.java
@@ -194,7 +194,8 @@ public class VTree extends AbstractTreeIndex {
     public IIndexBulkLoader createBulkLoader(float fillFactor, boolean verifyInput, long numElementsHint,
             boolean checkIfEmptyIndex, ISketchSampler sampler, IPageWriteCallback callback)
             throws HyracksDataException {
-        throw new UnsupportedOperationException();
+        throw HyracksDataException.create(ErrorCode.INVALID_OPERATOR_OPERATION, "createBulkLoader",
+                VTree.class.getSimpleName());
     }
 
     /**
@@ -1188,7 +1189,8 @@ public class VTree extends AbstractTreeIndex {
         @Override
         public void update(ITupleReference tuple) throws HyracksDataException {
             ctx.setOperation(IndexOperation.UPDATE);
-            throw new UnsupportedOperationException();
+            throw HyracksDataException.create(ErrorCode.INVALID_OPERATOR_OPERATION, "update",
+                    VTree.class.getSimpleName());
         }
 
         @Override
@@ -1200,7 +1202,8 @@ public class VTree extends AbstractTreeIndex {
 
         @Override
         public void upsert(ITupleReference tuple) throws HyracksDataException {
-            throw new UnsupportedOperationException();
+            throw HyracksDataException.create(ErrorCode.INVALID_OPERATOR_OPERATION, "upsert",
+                    VTree.class.getSimpleName());
         }
 
         @Override
@@ -1383,7 +1386,8 @@ public class VTree extends AbstractTreeIndex {
         @Override
         public void diskOrderScan(ITreeIndexCursor cursor) throws HyracksDataException {
             ctx.setOperation(IndexOperation.DISKORDERSCAN);
-            throw new UnsupportedOperationException("Disk order scan not yet implemented");
+            throw HyracksDataException.create(ErrorCode.INVALID_OPERATOR_OPERATION, "diskOrderScan",
+                    VTree.class.getSimpleName());
         }
 
         public VTreeOpContext getOpContext() {
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeBulkLoader.java b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeBulkLoader.java
index d7698af75d..143caf7f0a 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeBulkLoader.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeBulkLoader.java
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.hyracks.api.dataflow.value.ISerializerDeserializer;
+import org.apache.hyracks.api.exceptions.ErrorCode;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.data.std.primitive.IntegerPointable;
 import org.apache.hyracks.data.std.primitive.LongPointable;
@@ -377,7 +378,8 @@ public class VTreeBulkLoader extends PageWriteFailureCallback implements IIndexB
         } catch (HyracksDataException e) {
             throw e;
         } catch (Exception e) {
-            throw new HyracksDataException("Failed to create directory entry", e);
+            throw HyracksDataException.create(ErrorCode.VECTOR_INDEX_BUILD_FAILED, e,
+                    "Failed to create a directory entry");
         }
     }
 
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeFlushLoader.java b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeFlushLoader.java
index 71cac4e50e..7670dbe9ff 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeFlushLoader.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeFlushLoader.java
@@ -18,6 +18,7 @@
  */
 package org.apache.hyracks.storage.am.vector.impls;
 
+import org.apache.hyracks.api.exceptions.ErrorCode;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.data.std.primitive.LongPointable;
 import org.apache.hyracks.dataflow.common.data.accessors.ITupleReference;
@@ -73,7 +74,8 @@ public class VTreeFlushLoader extends PageWriteFailureCallback implements IIndex
 
     @Override
     public void add(ITupleReference tuple) throws HyracksDataException {
-        throw new UnsupportedOperationException("Use copyPage() instead");
+        throw HyracksDataException.create(ErrorCode.INVALID_OPERATOR_OPERATION, "add (use copyPage instead)",
+                VTreeFlushLoader.class.getSimpleName());
     }
 
     /**
@@ -237,7 +239,9 @@ public class VTreeFlushLoader extends PageWriteFailureCallback implements IIndex
 
     @Override
     public void end() throws HyracksDataException {
-        throw new UnsupportedOperationException("Use end(numLeafCentroid, firstLeafCentroidId, rootPageId) instead");
+        throw HyracksDataException.create(ErrorCode.INVALID_OPERATOR_OPERATION,
+                "end (use end(numLeafCentroid, firstLeafCentroidId, rootPageId) instead)",
+                VTreeFlushLoader.class.getSimpleName());
     }
 
     @Override
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeSearchCursor.java b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeSearchCursor.java
index e7019dbdd1..8a1e1aefe4 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeSearchCursor.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeSearchCursor.java
@@ -27,6 +27,7 @@ import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
 
+import org.apache.hyracks.api.exceptions.ErrorCode;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.dataflow.common.data.accessors.ITupleReference;
 import org.apache.hyracks.storage.am.common.api.ITreeIndexFrameFactory;
@@ -291,6 +292,7 @@ public class VTreeSearchCursor extends EnforcedIndexCursor {
         // value means the caller bypassed that path and the search cannot proceed safely.
         this.distanceFunction = vectorState.getDistanceFunction();
         if (this.distanceFunction == null) {
+            // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
             throw new IllegalStateException(
                     "VTreeSearchCursor opened without a distance function on the initial state");
         }
@@ -306,8 +308,8 @@ public class VTreeSearchCursor extends EnforcedIndexCursor {
             // Query mode: Find closest cluster using DFS
             // NOTE: Level-wise exploration is handled by LSM layer via openClusterById()
             if (this.queryVector == null) {
-                throw HyracksDataException
-                        .create(new IllegalArgumentException("Query vector must be provided for centroid finding"));
+                throw HyracksDataException.create(ErrorCode.ILLEGAL_STATE,
+                        "A query vector is required for vector index centroid finding");
             }
 
             // Create navigation state for iterative DFS with shared visited set
@@ -370,9 +372,11 @@ public class VTreeSearchCursor extends EnforcedIndexCursor {
     @Override
     protected void doNext() throws HyracksDataException {
         if (!isOpen) {
+            // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
             throw HyracksDataException.create(new IllegalStateException("Cursor is not open"));
         }
         if (!doHasNext()) {
+            // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
             throw HyracksDataException.create(new IllegalStateException("No more tuples"));
         }
 
@@ -423,6 +427,9 @@ public class VTreeSearchCursor extends EnforcedIndexCursor {
         this.totalLeafClusters = totalClusters;
 
         if (this.totalLeafClusters == 0) {
+            // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
+            // An empty vector index is arguably a normal state a query should tolerate rather than an
+            // error; decide whether this should return "no results" instead.
             throw HyracksDataException.create(new IllegalStateException("No leaf centroids found - empty index"));
         }
 
@@ -466,6 +473,7 @@ public class VTreeSearchCursor extends EnforcedIndexCursor {
                 if (interiorFrame.getTupleCount() > 0) {
                     currentPageId = interiorFrame.getChildPageId(0);
                 } else {
+                    // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
                     throw HyracksDataException
                             .create(new IllegalStateException("Empty interior page encountered during navigation"));
                 }
@@ -858,6 +866,7 @@ public class VTreeSearchCursor extends EnforcedIndexCursor {
      */
     private IVTreeMetadataFrame createMetadataFrame() {
         if (metadataFrameFactory == null) {
+            // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
             throw new IllegalStateException("Metadata frame factory not set");
         }
         return (IVTreeMetadataFrame) metadataFrameFactory.createFrame();
@@ -868,6 +877,7 @@ public class VTreeSearchCursor extends EnforcedIndexCursor {
      */
     private IVTreeDataFrame createDataFrame() {
         if (dataFrameFactory == null) {
+            // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
             throw new IllegalStateException("Data frame factory not set");
         }
         return (IVTreeDataFrame) dataFrameFactory.createFrame();
@@ -996,6 +1006,7 @@ public class VTreeSearchCursor extends EnforcedIndexCursor {
      */
     private IVTreeInteriorFrame createInteriorFrame() {
         if (interiorFrameFactory == null) {
+            // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
             throw new IllegalStateException("Interior frame factory not set");
         }
         return (IVTreeInteriorFrame) interiorFrameFactory.createFrame();
@@ -1003,6 +1014,7 @@ public class VTreeSearchCursor extends EnforcedIndexCursor {
 
     private IVTreeLeafFrame createLeafFrame() {
         if (leafFrameFactory == null) {
+            // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
             throw new IllegalStateException("Leaf frame factory not set");
         }
         return (IVTreeLeafFrame) leafFrameFactory.createFrame();
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeStaticStructureBuilder.java b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeStaticStructureBuilder.java
index 9ae0488944..fbefdc002d 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeStaticStructureBuilder.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/impls/VTreeStaticStructureBuilder.java
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.TreeSet;
 
+import org.apache.hyracks.api.exceptions.ErrorCode;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.data.std.primitive.IntegerPointable;
 import org.apache.hyracks.data.std.primitive.LongPointable;
@@ -237,6 +238,7 @@ public class VTreeStaticStructureBuilder extends PageWriteFailureCallback implem
         int childClusterIndex = computeChildClusterIndex();
         int childPageId = firstPageIdOfCluster[currentLevel + 1][childClusterIndex];
         if (childPageId < 0) {
+            // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
             throw new IllegalStateException(
                     "Child page id not yet recorded for level=" + (currentLevel + 1) + ", cluster=" + childClusterIndex
                             + ". Input must be in bottom-up order (leaf level first, root last).");
@@ -302,7 +304,8 @@ public class VTreeStaticStructureBuilder extends PageWriteFailureCallback implem
             return entryTupleRef;
         } catch (Exception e) {
             LOGGER.log(Level.TRACE, "Error creating entry tuple: {}", e.getMessage());
-            throw new HyracksDataException("Failed to create entry tuple", e);
+            throw HyracksDataException.create(ErrorCode.VECTOR_INDEX_BUILD_FAILED, e,
+                    "Failed to create an entry tuple");
         }
     }
 
@@ -528,6 +531,7 @@ public class VTreeStaticStructureBuilder extends PageWriteFailureCallback implem
         // also the page with the highest id since the root level is processed last.
         int rootPageId = firstPageIdOfCluster[0][0];
         if (rootPageId < 0) {
+            // TODO(vector-errors): uncoded IllegalStateException -> reaches the user as "Internal error".
             throw HyracksDataException
                     .create(new IllegalStateException("Root page id was never recorded; static structure incomplete."));
         }
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/utils/NoOpVectorQuantizer.java b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/utils/NoOpVectorQuantizer.java
index 33a97bb043..dcdd4252fd 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/utils/NoOpVectorQuantizer.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-vtree/src/main/java/org/apache/hyracks/storage/am/vector/utils/NoOpVectorQuantizer.java
@@ -20,6 +20,7 @@ package org.apache.hyracks.storage.am.vector.utils;
 
 import java.nio.ByteBuffer;
 
+import org.apache.hyracks.api.exceptions.ErrorCode;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.storage.am.vector.api.IVTreeQuantizer;
 
@@ -43,8 +44,8 @@ public class NoOpVectorQuantizer implements IVTreeQuantizer {
         // caller failed to strip a length prefix (or passed a truncated buffer). Fail loudly here
         // (test-only helper) rather than silently dropping trailing bytes.
         if (quantizedBytes.length % Double.BYTES != 0) {
-            throw new IllegalArgumentException("quantizedBytes length " + quantizedBytes.length
-                    + " is not a multiple of Double.BYTES (" + Double.BYTES + ")");
+            throw HyracksDataException.create(ErrorCode.ILLEGAL_STATE, "The quantized vector is "
+                    + quantizedBytes.length + " bytes, which is not a multiple of " + Double.BYTES);
         }
         int count = quantizedBytes.length / Double.BYTES;
 
diff --git a/hyracks-fullstack/hyracks/hyracks-test-support/src/main/java/org/apache/hyracks/storage/am/vector/TestDoubleArrayVectorAccessor.java b/hyracks-fullstack/hyracks/hyracks-test-support/src/main/java/org/apache/hyracks/storage/am/vector/TestDoubleArrayVectorAccessor.java
index 98ec7e321e..698c76072a 100644
--- a/hyracks-fullstack/hyracks/hyracks-test-support/src/main/java/org/apache/hyracks/storage/am/vector/TestDoubleArrayVectorAccessor.java
+++ b/hyracks-fullstack/hyracks/hyracks-test-support/src/main/java/org/apache/hyracks/storage/am/vector/TestDoubleArrayVectorAccessor.java
@@ -22,6 +22,7 @@ package org.apache.hyracks.storage.am.vector;
 import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
 
+import org.apache.hyracks.api.exceptions.ErrorCode;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.api.io.IPersistedResourceRegistry;
 import org.apache.hyracks.dataflow.common.data.marshalling.DoubleArraySerializerDeserializer;
@@ -64,7 +65,8 @@ public class TestDoubleArrayVectorAccessor implements IVTreeBinaryAccessor {
     public int getDimension() throws HyracksDataException {
         // Format: [int:length][double:v0]...
         if (length < Integer.BYTES) {
-            throw new HyracksDataException("Invalid vector data: too short to read dimension");
+            throw HyracksDataException.create(ErrorCode.UNEXPECTED_VECTOR_VALUE,
+                    "The vector data is too short to read a dimension");
         }
         return ((data[start] & 0xFF) << 24) | ((data[start + 1] & 0xFF) << 16) | ((data[start + 2] & 0xFF) << 8)
                 | (data[start + 3] & 0xFF);