appwrite/appwrite · error · Appwrite\Extend\Exception
general_cursor_not_found
general_cursor_not_found
Error message
Deployment '{$deploymentId}' for the 'cursor' value not found. What it means
The cursor value passed syntax validation, but the deployments lookup for that ID returned an empty document: the cursor deployment was deleted (retention cleanup, concurrent delete) or never existed. Cursor pagination anchors to an existing neighbor document, so the query cannot proceed.
Source
Thrown at src/Appwrite/Platform/Modules/Functions/Http/Deployments/XList.php:112
// Set resource queries
$queries[] = Query::equal('resourceInternalId', [$function->getSequence()]);
$queries[] = Query::equal('resourceType', ['functions']);
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
if ($cursor !== false) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$deploymentId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('deployments', $deploymentId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Deployment '{$deploymentId}' for the 'cursor' value not found.");
}
$cursor->setValue($cursorDocument);
}
$grouped = Query::groupByType($queries);
$filterQueries = $grouped['filters'];
$selectQueries = $grouped['selections'];
try {
$results = $dbForProject->find('deployments', $queries);
$total = $includeTotal ? $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT) : 0;
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
View on GitHub (pinned to a1d520eea4)
Solutions
- Restart pagination from the first page without the cursor
- Always use a cursor taken from the most recent list response
- Raise deployments retention or complete pagination faster than cleanup runs
- Fall back to limit/offset paging when a stable snapshot is acceptable
Defensive patterns
Strategy: validation
Validate before calling
// Use cursors only from a list response fetched moments ago:
const page = await functions.listDeployments({ functionId, queries: [Query.limit(25)] });
const last = page.deployments.at(-1);
if (!last) return; // empty page — done
const next = await functions.listDeployments({
functionId,
queries: [Query.cursorAfter(last.$id), Query.limit(25)],
}); Try / catch
try {
const res = await functions.listDeployments({ functionId, queries });
} catch (e) {
if (e?.code === 'general_cursor_not_found') {
// Cursor deployment was deleted — restart from page one without the cursor.
}
throw e;
} Prevention
- Do not persist cursors across long-lived sessions
- Complete pagination faster than retention deletes rows, or page by $createdAt ranges instead
When it happens
Trigger: Cursor references a deployment removed by retention or a concurrent DELETE between two pages; cursor reused from a stale cached list response; cursor ID typo'd or from another project/function.
Common situations: Long-running pagination over deployments while builds churn and old rows are purged; resuming pagination sessions from cached state; sharing cursors across environments.
Related errors
- general_cursor_not_found
- general_cursor_not_found
- database_query_order_null
- general_cursor_not_found
- general_cursor_not_found
AI-assisted analysis of appwrite/appwrite@a1d520eea4 (2026-08-18).
Data as JSON: /api/errors/086cf345f8473868.
Report an issue: GitHub.