Applies to VAST Cluster 5.5. Examples use the VAST DataBase Python SDK 2.1.0.
This page lists the errors the vastdb Python SDK (2.1.0) raises, what causes each one, and how to fix it. Search the page for the last line of your traceback.
Reading an SDK error
Most SDK exceptions print their fields as a Python dict:
vastdb.errors.MissingTable: {'bucket': 'my-bucket', 'schema': 'my-schema', 'table': 'my-table'}
(The values are placeholders.)
Errors returned by the cluster are subclasses of vastdb.errors.HttpError. Each one has these fields:
| Field | Contents |
|---|---|
code |
The S3-style error code returned by the cluster, if any (for example SlowDown) |
message |
The cluster's error message, if any |
status |
HTTP status code |
method, url |
The request that failed |
headers |
Response headers |
The class tells you the HTTP status:
| Class | Status |
|---|---|
BadRequest |
400 |
Forbidden |
403 |
NotFound |
404 |
MethodNotAllowed |
405 |
RequestTimeout |
408 |
Conflict |
409 |
InternalServerError |
500 |
NotImplemented |
501 |
ServiceUnavailable |
503 |
Slowdown |
503 with code SlowDown |
InsufficientCapacity |
507 |
UnexpectedError |
any other status |
When you report a problem, include code, message, status and url.
The SDK also logs to stderr, even when your code catches the exception. On a failed request it logs RPC failed: {...}, with the full URL (including your endpoint address) and the response headers. When a transaction rolls back, it logs rolling back txid=... with the traceback (see Python SDK). Remove addresses and keys before you paste these logs anywhere public.
Catching errors by family: you can catch a whole group with one base class, for example except vastdb.errors.Missing:.
| Base class | Covers |
|---|---|
Missing |
MissingBucket, MissingSchema, MissingTable, MissingProjection, MissingSnapshot, MissingBlobExpansion, MissingTransaction, MissingRowIdColumn |
Exists |
SchemaExists, TableExists |
InvalidArgument |
TooLargeRequest, and TooWideRow beneath it |
NotSupported |
NotSupportedVersion, NotSupportedSchema, NotSupportedCommand |
HttpError |
Every error returned by the cluster |
Connecting
ValueError: One of endpoint and AWS_S3_ENDPOINT_URL env is required
You didn't pass endpoint to vastdb.connect(), and AWS_S3_ENDPOINT_URL isn't set.
Fix: pass endpoint="http://<vip-or-dns-name>". You get the address from your platform team; see setup prerequisites.
KeyError: 'AWS_ACCESS_KEY_ID' (or 'AWS_SECRET_ACCESS_KEY')
You didn't pass access or secret, and the matching environment variable isn't set.
Fix: pass access= and secret=, or export both variables.
vastdb.errors.ConnectionError: {'cause': ConnectionError(MaxRetryError(... Failed to establish a new connection: [Errno 111] Connection refused ...)), 'may_retry': True}
The SDK couldn't reach the endpoint. connect() contacts the endpoint straight away, to find out the cluster version, so a wrong address or port fails at connect time. If the request failed with a timeout, DNS failure or TLS error, cause says so instead of "Connection refused".
Fix:
- Check the endpoint's host and port, and that it uses
http://orhttps://. - Check that your client can reach the address: a firewall, VPN or wrong VIP pool are common causes.
- For TLS certificate failures, use the CA bundle your platform team gives you.
ssl_verify=Falseonly belongs in tests.
may_retry: True means the SDK already retried this request according to your BackoffConfig before giving up.
vastdb._internal.UnsupportedServer: <url> is not a VAST DB server endpoint ("<Server header>")
Something answered at the endpoint, but it isn't a VAST DataBase endpoint. Common causes are a load balancer, a proxy, or another service on that address. If you've used the cluster's management (VMS) address, the message ends with ("nginx").
Fix: point the SDK at a VIP from a pool that serves the database, not a management address.
NotImplementedError: Please use `vastdb` <= 0.0.5.x with current VAST cluster version ("<Server header>"). To use the latest SDK, please upgrade your cluster to the latest service pack. ...
The endpoint is a VAST cluster, but its version string is in a format this SDK doesn't recognise. In practice that means the cluster is too old for this SDK.
Fix: check which SDK versions your cluster supports in versions, and pin a compatible vastdb.
Feature not supported by your cluster
The SDK checks your cluster's version before it uses a version-dependent feature, and raises NotSupportedVersion without sending a request. Some of these messages use internal codenames: "Hydra" means partitioned tables and "Elysium" means sorted tables.
| Message | What you tried | Minimum cluster |
|---|---|---|
{'err_msg': 'Hydra requires 5.5.0+ VAST release', 'version': (5, 4)} |
A partitioned-table operation, such as Session.buffered_inserter() |
5.5 |
{'err_msg': 'Blob expansion requires 5.5+ VAST release', ...} |
create_blob_expansion() / blob_expansion() |
5.5 |
{'err_msg': 'Vector tables require 5.5+ VAST release', ...} |
Loading a vector-indexed table | 5.5 |
{'err_msg': 'Elysium requires 5.3.5+ VAST release', ...} |
add_sorting_key() |
See note below |
{'err_msg': 'Zip import requires 5.3.1+ VAST release', ...} |
ImportConfig(key_names=...) |
5.3.1 |
{'err_msg': "Imported objects' table feature requires 5.2+ VAST release", ...} |
create_imports_table() / imports_table() |
5.2 |
{'err_msg': 'External row IDs allocation requires 5.1+ VAST release', ...} |
create_table(use_external_row_ids_allocation=True) |
5.1 |
{'err_msg': 'Semi-sorted projection enforcement requires 5.1+ VAST release', ...} |
QueryConfig(semi_sorted_projection_name=...) |
5.1 |
In each message, version is the cluster version the SDK detected.
Note on sorted tables: the message says 5.3.5, but SDK 2.1.0 only checks for 5.3.
Fix: ask your platform team for the cluster version, and check the versions matrix. Upgrading the cluster is an admin task.
Missing or existing objects
| Error | Cause | Fix |
|---|---|---|
MissingBucket: {'bucket': ...} |
tx.bucket(name) found no bucket with that name (the cluster answered 404). If the bucket exists but your key isn't allowed to use it, you get Forbidden instead. |
Check the name. To create the bucket, ask your platform team; see setup prerequisites. |
MissingSchema: {'bucket': ..., 'schema': ...} |
The schema doesn't exist in that bucket. Nested schemas use /-separated paths. |
Create it, or call schema(name, fail_if_missing=False) and handle None. |
MissingTable: {'bucket': ..., 'schema': ..., 'table': ...} |
The table doesn't exist in that schema. | Check the names, or use fail_if_missing=False. |
MissingProjection: {..., 'projection': ...} |
No semi-sorted projection has that name. | List the projections with table.projections(). |
MissingSnapshot: {'bucket': ..., 'snapshot': '.snapshot/<name>'} |
No snapshot has that name on the bucket. | List the snapshots with bucket.snapshots(). |
MissingBlobExpansion |
The table has no blob expansion for that source column. | See blob expansion. |
SchemaExists / TableExists |
A create call used fail_if_exists=True (the default) and the object already exists. |
Pass fail_if_exists=False to get the existing object. Note that a table's definition isn't compared, so an existing table with different columns is returned unchanged. |
Name lookups happen inside your transaction, so an object created by a transaction that hasn't committed yet may not be found.
Permission and cluster errors
vastdb.errors.Forbidden (HTTP 403)
Your access key's identity policy doesn't allow the action. Each SDK operation needs a particular s3:Tabular* action. Queries that go through the VAST Query Engine also need s3:QueryEngine*.
You'll see one of these forms:
| What you see | When |
|---|---|
code: 'SignatureDoesNotMatch' or code: 'InvalidAccessKeyId', from vastdb.connect() |
The secret key is wrong, or the cluster doesn't know the access key. connect() sends a signed request straight away, so bad credentials fail there. |
code: None, message: None, method: 'HEAD', and a url that ends in your bucket name |
Your key's identity policy doesn't allow s3:ListBucket on the bucket, or the key has no policy at all. The first call that touches the bucket, tx.bucket(name), fails. A policy with only s3:Tabular* actions isn't enough. |
code: 'AccessDenied' and a message such as [req=<id>]: Permission denied for @s3_bucket_name=<bucket> @s3_object_name=<schema> - failing request (from create_schema) or [req=<id>]: Permission check failed for @s3_object_name=<schema>/<table> table_@handle=<id> (from insert) |
Your key has some Tabular permissions, but not the one this operation needs. For example, a key that can list and query, but not create or insert. |
The message names the object, not the missing action. To work out the action, use the operation and the url: the part after ? (for example ?schema or ?rows) shows the kind of request.
Fix: send your platform team the operation that failed, plus the url, method and message from the error. They'll need to grant the matching action. The [req=...] value in the message identifies the request on the cluster. Setup prerequisites maps SDK operations to Tabular actions.
vastdb.errors.NotSupportedSchema
The cluster returned TabularUnsupportedColumnType, meaning a column type in your schema isn't supported. The SDK also raises this error itself, with message="Ibis does not support the schema unsupported_fields=[...]", when Ibis can't represent a column type. The cause field holds the original error.
Fix: change the column type. See data types for what's supported.
vastdb.errors.Slowdown / ServiceUnavailable (HTTP 503)
Slowdown means the cluster is throttling requests. The SDK retries it automatically, so you only see it once the retry budget in BackoffConfig runs out (by default 10 tries or 60 seconds). Other 503 errors aren't retried.
Fix: reduce concurrency. That means fewer data_endpoints threads, fewer parallel transactions and lower import concurrency. Or raise BackoffConfig.max_time. If it keeps happening, tell your platform team; they can check cluster load.
vastdb.errors.InsufficientCapacity (HTTP 507)
The cluster has run out of capacity for the request. This is not something you can fix from the client. Admin task: send to your platform team, with the full error.
vastdb.errors.BadRequest (HTTP 400) with code TabularTxAlreadyRollbacked, at commit
The message is Tabular transaction already rollbacked. The cluster had already rolled your transaction back because of a conflict with another transaction, so the commit fails. You also get this if you catch a Conflict inside the transaction and carry on.
Fix: don't catch Conflict inside the transaction it happened in. Let the transaction fail, then retry the whole transaction. See transactions in practice.
vastdb.errors.Conflict (HTTP 409)
The cluster rejected the request because another open transaction has already made a conflicting change to the same object. The error has code: 'TabularWwc'.
These cases were captured with two transactions open at the same time, where the second one made the same change as the first:
| Second transaction's call | message |
|---|---|
create_schema(name) |
[req=<id>]: failed to create_estore_object for schema=./<schema> @s3_bucket_name=<bucket> |
create_table(name, ...) |
[req=<id>]: creating table=<table> EStore object under @handle=<id> failed (res=TABULAR_WWC) |
add_column(...) with the same column name |
[req=<id>]: Failed add column name=<column> in table_@handle=<id> eres=TABULAR_WWC |
The error is raised by the call itself, not when the transaction commits. The transaction context manager then rolls back that transaction. The first transaction isn't affected.
Fix: avoid running the same DDL from several clients at once. If you have to, catch vastdb.errors.Conflict and decide whether the change is already done, for example by calling schema(name, fail_if_missing=False). For retry patterns, see transactions in practice.
Queries and predicates
NotImplementedError showing your predicate expression
NotImplementedError: r0 := UnboundTable: t
a int64
b int64
...
Or(Greater(a, 1), Greater(b, 1)): r0.a > 1 | r0.b > 1
The SDK couldn't send your predicate to the cluster, and it raises this before any request is made. The expression at the end of the message is the part it couldn't send. These are the common causes:
| Pattern | Example | Rewrite |
|---|---|---|
| An OR across different columns | (t["a"] > 1) \| (t["b"] > 1) |
Every term in an OR must use the same column. Run two queries, or filter in Arrow after a broader pushdown. |
| Comparing two columns | t["a"] > t["b"] |
Push down a literal bound instead, and compare the columns client-side. |
NOT, except around isnull() |
~(t["a"] > 1) |
Write the opposite comparison: t["a"] <= 1. Note that this treats NULLs differently. |
A predicate on a nested column (a list, or a struct that isn't a leaf) fails with the column's field instead of the expression:
NotImplementedError: pyarrow.Field<y: list<item: int64>>
The full rules are in Predicates.
vastdb.transaction.TransactionNotActiveError
You used a table handle, or another object from a transaction, after the with session.transaction() block ended. Objects belong to the transaction they came from.
Fix: get the table again inside a new transaction, or reuse its metadata; see transactions in practice.
vastdb.errors.MissingTransaction
You read a query result after its transaction had closed. select() streams its results, so the reader has to be consumed inside the with session.transaction() block.
Fix: call .read_all(), or finish iterating the reader, before the block ends.
vastdb.errors.TooLargeRequest: <n> bytes
The query request itself is too large: the serialised predicate and column list exceed about 90% of the 5 MiB request limit. The usual cause is a very large isin([...]) list.
Fix: split the value list over several queries, or load the values into a table and filter differently.
vastdb._internal.QueryDataInternalError: Query failed due to server error: error_code=<n>
The cluster aborted the query and returned an error code. Admin task: send to your platform team, with the error code, the table and the time it happened.
NotImplementedError: Method select_splits is not implemented for partitioned tables
select_splits() doesn't support partitioned tables in 2.1.0. Use select() instead.
ValueError: TableType was not loaded. load using TableMetadata.load_stats
You called select() on a table built with tx.table_from_metadata(md) from a TableMetadata that was never loaded. Constructing it with arrow_schema= alone doesn't count as loading it. select() needs to know the table type, and that comes from md.load(tx). insert() and import_files() don't need it.
Fix: call md.load(tx) once, then reuse md in later transactions.
ValueError: limit_rows must be a non-negative integer or None
You passed a negative limit_rows.
Writes
vastdb.errors.MissingRowIdColumn
You called update() or delete() with rows that have no $row_id column.
Fix: select the rows with internal_row_id=True first, and pass the result (or its $row_id column) in. See update and delete.
vastdb.errors.BadRequest (HTTP 400) with code InvalidArgument from update()
The message is just Invalid Argument. You tried to change a column that can't be updated: a sort column, a partition column (to a value in another partition), the vector column, or vastdb_rowid. The SDK doesn't check this before it sends the update.
Fix: delete the row and insert it again. See columns you can't update.
vastdb.errors.NotFound (HTTP 404) with code TabularMismatchColumnName from update()
The rows you passed to update() have a column the table doesn't have. The message talks about a "file to import", even though this is an update.
Fix: pass only the table's columns plus $row_id, or name the columns with update(rows, columns=[...]).
vastdb.errors.BadRequest (HTTP 400) with code TabularUndroppableColumn from drop_column()
The message starts failed to drop columns for @s3_object_name=<schema>/<table>. You tried to drop a column that can't be dropped: a sort column of a sorted table, or vastdb_rowid once the table has user-defined row IDs.
Fix: keep the column. To lose a sort column, copy the data into a new table without it. See adding and dropping columns.
vastdb.errors.BadRequest (HTTP 400) with code InvalidArgument: adding columns on indexed tables is forbidden
Or dropping columns on indexed tables is forbidden. You called add_column() or drop_column() on a vector-indexed table. Its columns can't change.
Fix: create a new table with the columns you need, and copy the data into it. See table structures.
KeyError from select(columns=[...]) after a column was dropped
In a transaction that started after the drop committed, the SDK doesn't know the column, and raises KeyError on the client before sending the query. A transaction that started before the drop can still read the column.
Fix: remove the column from columns=, and reload any TableMetadata you cached before the drop.
vastdb.errors.TooWideRow: pyarrow.RecordBatch ...
(truncated)
vastdb.errors.TooWideRow: pyarrow.RecordBatch
blob: binary
----
[...]
At least one row can't fit in a single request, even with the SDK splitting by columns. The message prints the entire batch, which can be very long. The insert path raises the same error with the message One of the rows in the given batch is too big for an rpc.
The error can come after the SDK has already written the row's first columns. If you catch it inside the transaction and let the transaction commit, you commit a partial row (see ingest). Let the exception roll the transaction back.
Fix: keep large values out of individual rows. Store large objects as files, and keep their paths in the table. For cell and request size limits, see limits.
Partitioned tables
| Message | Cause |
|---|---|
AssertionError: partitions can only be used on a partitioned table |
You called partitions() or delete_partitions() on a table that isn't partitioned. |
AssertionError: Beware, deleting partitions is a non acid operation! In order to proceed please use allow_non_acid=True |
delete_partitions() requires you to accept that it isn't transactional. Pass allow_non_acid=True only if a partial delete is acceptable to you. |
ValueError: Table must be partitioned to use a buffered inserter |
Session.buffered_inserter() was given a table that isn't partitioned. Use insert() instead. |
AssertionError: Cannot use the buffered inserter after call to finish |
You called add_batch() after finish(), or after the with block ended. |
ValueError: A partitioned table can be partitioned on no more than 4 keys |
Your PartitionSpec has more than 4 keys. |
ValueError: Partition key column '<name>' does not exist in the provided Arrow schema |
A PartitionKey names a column that isn't in the table's schema. |
Creating tables and sort keys
| Message | Cause |
|---|---|
ValueError: Arrow schema contains duplicate field names: [...] |
The schema passed to create_table() repeats a field. |
ValueError: The following fields ['<name>'] don't exist in the given arrow schema |
A sorting_key names a column that isn't in the schema. |
ValueError: Adding a sorting key needs at least one column |
You called add_sorting_key([]). |
vastdb.errors.NotSupportedCommand: {'bucket': ..., 'schema': ..., 'table': ...} |
You ran a write or DDL operation on a table's imports table, which only supports reads. |
Importing Parquet files
vastdb.errors.InvalidArgument: Path <path> must start with a '/'
Import paths have the form /<bucket>/<key>, for example /my-bucket/staging/part-0001.parquet.
vastdb.errors.NotFound (HTTP 404) from import_files()
The object you asked to import doesn't exist, or its bucket doesn't exist. This doesn't raise ImportFilesError: you get a NotFound with code: None and message: None, and its url is your table's URL with ?data on the end, not the file's. So the error doesn't say which file is missing.
Fix: check every path in the list. Each one is /<bucket>/<key>, with the bucket name first.
vastdb.errors.ImportFilesError
The cluster reported a failure while importing a file. The message field has this form:
Encountered an error during import_data. status: <status>, error message: <message> during import of object name: <object>
error_dict holds the cluster's full status record. Its res field is the status. These were captured:
res (status) |
Error message | Cause |
|---|---|---|
TabularMismatchColumnType |
Failed to generate_arrow_read_paths for field column=<column> type=<file type> on table @handle=<id> |
A column in the file has a different type from the table's column with the same name. |
TabularMismatchColumnName |
Failed to generate_arrow_read_paths for field column=<column> type=<file type> on table @handle=<id> |
The file has a column the table doesn't have. |
InvalidArgument |
Unexpected error |
The object isn't a valid Parquet file. |
The two mismatch statuses have the same error message, so check res, or the status: part of message. The column= part names the file's column, and type= is its type in the file.
A missing object raises NotFound instead; see above.
Fix: check the file's columns and types against table.columns(). Files with extra columns need to be rewritten, or the table altered first.
vastdb.errors.InvalidArgument: Found mismatch in parquet files schemas.
util.create_table_from_files() uses default_schema_merge, which requires each file's schema to be a subset of the largest one. If you use strict_schema_merge instead, the message is Schemas are not identical. followed by both schemas.
Fix: use union_schema_merge to combine the schemas, or fix the files.
Configuration
These are all raised when you construct the object, before anything is sent.
| Message | Raised by |
|---|---|
ValueError: data_endpoints cannot be empty if provided |
QueryConfig(data_endpoints=[]), ImportConfig(data_endpoints=[]). Pass None to use the defaults. |
ValueError: data_endpoints cannot be empty |
SessionConfig(data_endpoints=[]) |
ValueError: Capacity in bytes must be a positive integer |
BufferedInsertConfig(capacity_in_bytes<=0) |
ValueError: Timeout must be a positive duration. |
BufferedInsertConfig(timeout<=0) |
ValueError: Target number of rows per partition must be a positive integer |
BufferedInsertConfig(target_num_rows_per_partition<=0) |
ValueError: Provided transaction must be active |
BufferedInsertConfig(tx=...) with a transaction that isn't open |
ValueError: Cannot set a timeout when an explicit transaction is provided |
BufferedInsertConfig with both tx and timeout set |
Vector search and ADBC
vastdb.transaction.NoAdbcConnectionError: Adbc Driver may not have been supplied
You called vector_search() or tx.adbc_conn, but no ADBC driver was available when the transaction started. If the driver is missing when a transaction opens, the SDK opens it without ADBC and doesn't raise an error, so you only find out when you use it.
Fix: pip install 'vastdb[adbc]', or pass adbc_driver_path= to vastdb.connect().
AssertionError: Table is either not vector indexed. (maybe try reloading the TableMetadata)
You called vector_search() on a table with no vector index, or on stale metadata. For vector tables, see the VAST Vector Search.
Warnings
DeprecationWarning: sorted_columns is deprecated and will be removed in vastdb 3.0.0, ...
You called reload_sorted_columns(), which is deprecated, or Table.sorted_columns(), which isn't deprecated but triggers the same warning in 2.1.0. It's harmless.
DeprecationWarning: insert_in_column_batches(rows) is deprecated and will be removed in vastdb 3.0.0, use insert(rows) instead
Replace the call with insert(rows), which does the same splitting.