Skip to Content
Raw

Errors in the Aetherfy vector database

The Aetherfy error envelope

Every error the Aetherfy vector API authors is returned in one shape:

{ "error": { "code": "STABLE_CODE", "message": "human readable description" } }

code is the stable, machine-readable identifier — branch on it, never on the message text, which is written for humans and may be reworded. Alongside the two required fields, an Aetherfy error may carry extras that depend on the site that raised it:

Extra fieldAppears on
detailsErrors with structured context
field, maxValidation failures against a numeric cap
request_idCorrelation identifier for support
documentation_urlErrors with a canonical explanation
collection_nameCollection-scoped failures
existing_regionsA collection that lives in another region
agentsA collection still referenced by agents
size_mb, max_size_mbResponse-size failures
current_etagSchema compare-and-set conflicts
retriableErrors Aetherfy marks as safe to retry

The Aetherfy error envelope is universal

Every path authors the envelope, including the storage-engine pass-through routes. Those routes proxy Qdrant, whose native failure body is shaped {"status": {"error": "..."}, "time": 0.001} — Aetherfy normalises it on the way out rather than relaying it, so error.code is always present and a generic handler can rely on it.

Earlier versions of these docs described POST /api/v1/collections/{name}/points/retrieve as an exception that relayed the engine body verbatim. That is no longer true. If you wrote a handler tolerating a missing error.code on that route, the tolerance is now dead code — harmless, but you can drop it.

Error codes in the Aetherfy vector API

Every code Aetherfy can return, with its HTTP status and whether retrying the identical request can succeed.

CodeHTTPMeaningRetryable
MISSING_API_KEY401No Authorization header, or not a Bearer headerno
INVALID_API_KEY401The key does not resolveno
ACCOUNT_SUSPENDED403The account is suspended for billingno
COLLECTION_OWNERSHIP_MISMATCH403The collection belongs to another accountno
NOT_FOUND404Unsupported endpoint, or missing collectionno
COLLECTION_NOT_FOUND404No such collectionno
SCHEMA_NOT_DEFINED404The collection has no stored schemano
METHOD_NOT_ALLOWED405The method is not supported on that pathno
VALIDATION_ERROR400A request field failed validationno
INVALID_POINT_ID400A point id is not an unsigned integer or UUIDno
TOO_MANY_POINTS400More than 10 000 points in one upsertno
RESERVED_FIELD400A payload key begins __aetherfy_no
MALFORMED_JSON400The body is not valid JSONno
SCHEMA_VALIDATION_FAILED400The payload violates a strictly-enforced schemano
COLLECTION_LIMIT_EXCEEDED400The tier’s collection cap is reachedno
INVALID_ENFORCEMENT_MODE400Not off, warn, or strictno
INVALID_SAMPLE_SIZE400Outside 100 to 10 000no
REQUEST_IDLE_TIMEOUT40830 s elapsed with no bytes on an upsertno
COLLECTION_EXISTS_IN_OTHER_REGION409The collection already lives elsewhereno
COLLECTION_NAME_TAKEN409The name is in use by a collection with a different configuration — a re-create with the same configuration is idempotent and returns 200no
COLLECTION_IN_USE409An agent still references the collectionno
CLEANUP_IN_PROGRESS409A prior deletion is still finishing — carries retriable: trueyes
SCHEMA_VERSION_MISMATCH412If-Match ETag conflict; body carries current_etagno
PAYLOAD_TOO_LARGE400 / 413Two causes, two statuses: 400 when a single point’s payload exceeds 64 KB, 413 when the upsert wire body exceeds the 500 MB ceilingno
RESPONSE_TOO_LARGE413The response would exceed 10 MBno
COLLECTION_REGIONS_EMPTY422No regions resolved for the requestno
COLLECTION_REGIONS_NOT_IN_SCOPE422Requested regions are outside the plan’s scopeno
STARTER_REGION_CONSISTENCY422Single-region plan; the resource asked for a different regionno
RATE_LIMIT_EXCEEDED429The tier’s per-minute request cap is exceededyes
STORAGE_LIMIT_EXCEEDED429The tier’s storage cap is exceeded — free space firstno
INTERNAL_ERROR500Unhandled server-side failureno
PROXY_ERROR502The proxy could not complete the upstream callyes
SERVICE_UNAVAILABLE503The service is temporarily unable to serveyes
GATEWAY_TIMEOUT504The upstream call timed outyes

Aetherfy’s fallback error codes

The codes above are authored deliberately: something in Aetherfy decided that exact failure and named it. The codes below are different in kind. Aetherfy proxies a storage engine and authors the error envelope on the way out — when the upstream body carries a usable code, that code is preserved; when it does not, Aetherfy synthesises one from the HTTP status.

Seeing one of these means the upstream failed without naming its own reason, so the message carries more signal than the code. Treat them as “something upstream went wrong, here is roughly what kind” rather than as specific diagnoses.

CodeHTTPMeaningRetryable
BAD_REQUEST400The engine rejected the request and supplied no code. Read the messageno
UNAUTHORIZED401An upstream component refused credentials without naming a codeno
FORBIDDEN403An upstream component denied the request without naming a codeno
CONFLICT409A conflicting state upstream, unnamed. The specific 409s above are more preciseno
PRECONDITION_FAILED412A precondition failed upstream, unnamed — typically an ETag mismatchno
RATE_LIMITED429A 429 from upstream carrying no code of its ownyes
UPSTREAM_ERROR500 / 502 / 503 / 504The catch-all: an unmapped upstream status with no codeyes

RATE_LIMITED is not RATE_LIMIT_EXCEEDED

Both exist, both are 429, and they mean different things. Branch on the code, not the status:

CodeWho produced itWhat it tells you
RATE_LIMIT_EXCEEDEDAetherfy’s own per-minute rate limiterYou exceeded your plan’s request allowance. Back off; the allowance resets on the next minute bucket
RATE_LIMITEDThe envelope normaliser, from an upstream 429Something upstream of Aetherfy throttled the request without naming a code. Not necessarily your plan allowance

A retry loop keyed on status === 429 will treat these identically, which is usually fine — both want backoff. A dashboard or alert that reports “you hit your rate limit” should key on RATE_LIMIT_EXCEEDED specifically, or it will misattribute an upstream throttle to the customer’s plan.

Which Aetherfy errors are worth retrying

Only five Aetherfy codes can succeed on an identical retry. Everything else is a statement about the request or the account, and repeating it verbatim produces the same answer.

Retryable codeHTTPHow to retry against Aetherfy
CLEANUP_IN_PROGRESS409Wait for the prior deletion to finish; the body carries retriable: true
RATE_LIMIT_EXCEEDED429Back off on your own schedule — Aetherfy sends no Retry-After header
PROXY_ERROR502Retry with backoff
SERVICE_UNAVAILABLE503Retry with backoff
GATEWAY_TIMEOUT504Retry with backoff

STORAGE_LIMIT_EXCEEDED is a 429 but is not retryable: nothing frees itself while you wait. Delete data or raise the cap. See limits.

Exception classes in the Aetherfy Python SDK

The Aetherfy Python SDK maps API errors onto typed exceptions. The base class is AetherfyVectorsException; catch it to catch everything the SDK authors.

Attribute on the base classMeaning
.messageHuman-readable description
.request_idCorrelation identifier
.status_codeHTTP status
.detailsStructured context, when present
.error_codeThe stable code from the envelope
ExceptionExtra attributes
AetherfyVectorsExceptionbase class
AuthenticationError
RateLimitExceededError.retry_after
ServiceUnavailableError
ValidationError
CollectionNotFoundError
PointNotFoundError
RequestTimeoutError
NetworkError
SchemaValidationError.errors
SchemaNotFoundError
CollectionInUseError
CollectionInOtherRegionError.existing_regions, .requesting_region
QuotaExceededError.quota_type, .current, .limit
PartialUpsertError.saved, .total, .failed

.retry_after on RateLimitExceededError is an attribute of the class, not a value Aetherfy supplies: Aetherfy sends no Retry-After header on a 429, so there is nothing to populate it from. Do not branch on it — back off on your own schedule.

The helper is_retryable_error(error) lives in aetherfy_vectors.exceptions and answers the retry question for you.

import os import time from aetherfy_vectors import ( AetherfyVectorsClient, AetherfyVectorsException, AuthenticationError, QuotaExceededError, RateLimitExceededError, ) from aetherfy_vectors.exceptions import is_retryable_error client = AetherfyVectorsClient(api_key=os.environ["AETHERFY_API_KEY"]) for attempt in range(3): try: results = client.search("articles", query_vector=[0.1, 0.2, 0.3, 0.4], limit=5) print([hit.id for hit in results]) break except AuthenticationError as err: print("bad key, not retrying:", err.message) break except QuotaExceededError as err: print("quota:", err.quota_type, err.current, err.limit) break except RateLimitExceededError as err: print("rate limited, request_id:", err.request_id) time.sleep(2 ** attempt) except AetherfyVectorsException as err: if not is_retryable_error(err): print("permanent failure:", err.error_code, err.status_code) break time.sleep(2 ** attempt) client.close()

Exception classes in the Aetherfy JavaScript SDK

The Aetherfy JavaScript base class is AetherfyVectorsError — note the different name from Python’s AetherfyVectorsException.

Member on the base classMeaning
.codeThe stable code from the envelope
.requestIdCorrelation identifier
.statusCodeHTTP status
.detailsStructured context, when present
toJSON()Serialisable form of the error
ExceptionExtra members
AetherfyVectorsErrorbase class
AuthenticationError
RateLimitExceededError.retryAfter
ServiceUnavailableError
ValidationError
CollectionNotFoundError
PointNotFoundError
RequestTimeoutError
NetworkError
ConflictErrorno Python twin
CollectionInOtherRegionError
QuotaExceededError
CollectionInUseError
SchemaNotFoundError
SchemaValidationError
PartialUpsertError

.retryAfter on the Aetherfy JavaScript RateLimitExceededError is the same story as Python’s .retry_after: the member exists on the class, but because Aetherfy sends no Retry-After header there is nothing to populate it from. Do not branch on it — back off on your own schedule.

Three utilities ship alongside them from the Aetherfy package root: createErrorFromResponse, isAetherfyVectorsError, and isRetryableError.

import { AetherfyVectorsClient, AetherfyVectorsError, AuthenticationError, QuotaExceededError, RateLimitExceededError, isAetherfyVectorsError, isRetryableError, } from 'aetherfy-vectors'; const client = new AetherfyVectorsClient({ apiKey: process.env.AETHERFY_API_KEY }); const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); for (let attempt = 0; attempt < 3; attempt += 1) { try { const results = await client.search('articles', [0.1, 0.2, 0.3, 0.4], { limit: 5 }); console.log(results.map((hit) => hit.id)); break; } catch (err) { if (err instanceof AuthenticationError) { console.log('bad key, not retrying'); break; } if (err instanceof QuotaExceededError) { console.log('quota exceeded'); break; } if (err instanceof RateLimitExceededError) { console.log('rate limited, requestId:', err.requestId); await sleep(2 ** attempt * 1000); continue; } if (isAetherfyVectorsError(err)) { if (!isRetryableError(err)) { const e = err as AetherfyVectorsError; console.log('permanent failure:', e.code, e.statusCode); break; } await sleep(2 ** attempt * 1000); continue; } throw err; } } client.dispose();

The duplicate AuthenticationError in the Aetherfy JavaScript SDK

The Aetherfy JavaScript package defines two classes named AuthenticationError, in two modules. To keep the package root unambiguous, the one declared in src/auth.ts is re-exported under a different name:

Declared inRoot export name
src/exceptions.tsAuthenticationError
src/auth.tsAuthError

So AuthenticationError imported from the Aetherfy package root is the transport-level error raised when the API rejects a key, and AuthError is the key-management one. An instanceof check against the wrong class silently never matches, so import the name that corresponds to the layer you are catching.

Mapping Aetherfy error names across the two SDKs

ConceptPythonJavaScript
Base classAetherfyVectorsExceptionAetherfyVectorsError
Stable code.error_code.code
Correlation id.request_id.requestId
HTTP status.status_code.statusCode
Rate-limit attribute (never populated — Aetherfy sends no Retry-After header).retry_after.retryAfter
Retryability helperis_retryable_error (in aetherfy_vectors.exceptions)isRetryableError (package root)
Type guardisAetherfyVectorsError
Conflict classConflictError

The caps and tier limits behind most 4xx codes are on limits; the routes that raise them are on the REST API reference.

Last updated on