appwrite/appwrite · error · Appwrite\Extend\Exception

general_query_invalid

general_query_invalid

Error message

$e->getMessage()

What it means

Query::parse_queries() failed to turn the incoming 'queries' strings into Query objects, and the QueryException message is forwarded verbatim as general_query_invalid. The bulk delete route receives queries as raw strings (one per array element), so any method name, argument shape, or value encoding that the Query parser does not recognize is rejected before the deleteDocuments call.

Source

Thrown at src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php:118

        if ($collection->isEmpty()) {
            throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
        }

        $hasRelationships = \array_filter(
            $collection->getAttribute('attributes', []),
            fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
        );

        if ($hasRelationships) {
            throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk delete is not supported for ' . $this->getSDKNamespace() . ' with relationship attributes');
        }

        $originalQueries = $queries;

        try {
            $queries = Query::parseQueries($queries);
        } catch (QueryException $e) {
            throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
        }

        // Handle transaction staging
        if ($transactionId !== null) {
            $transaction = $dbForProject->getDocument('transactions', $transactionId);
            if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') {
                throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction');
            }

            // Enforce max operations per transaction
            $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION;
            $existing = $transaction->getAttribute('operations', 0);
            if (($existing + 1) > $maxBatch) {
                throw new Exception(
                    Exception::TRANSACTION_LIMIT_EXCEEDED,
                    'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch
                );
            }

View on GitHub (pinned to a1d520eea4)

Solutions

  1. Build queries with the SDK's Query class (Query.equal, Query.limit, Query.cursorAfter, ...) instead of hand-writing strings
  2. Read the forwarded parser message - it names the exact method or argument that failed to parse
  3. Keep within the documented limits (at most 100 query strings, each within the element size cap)
  4. Log the exact queries array you send when this fires, and replay it through the parser mentally: method("attr", [values])

Example fix

// before: hand-built query string with broken quoting
await sdk.databases.deleteDocuments(dbId, colId, ['equal("status",archived)']);

// after: let the SDK generate the query string
await sdk.databases.deleteDocuments(dbId, colId, [Query.equal('status', 'archived')]);
Defensive patterns

Strategy: validation

Validate before calling

// always build query strings with the SDK helpers instead of hand-writing them
import { Query } from 'node-appwrite';
const queries = [Query.equal('status', 'archived'), Query.limit(1000)];
await sdk.databases.deleteDocuments(dbId, colId, queries);

Type guard

function isWellFormedQueryString(q: string): boolean {
  // every element must look like method("attr", [args]) as produced by the SDK Query helpers
  return /^[a-zA-Z]+\([^)]*\)$/.test(q.trim()) && q.length <= 1000; // APP_LIMIT_ARRAY_ELEMENT_SIZE
}

Try / catch

try {
  await sdk.databases.deleteDocuments(dbId, colId, queries);
} catch (e) {
  if (e.code === 'general_query_invalid') {
    // e.message is the parser's own diagnostic - fix the offending string it names
    logger.error('query parse failed', { queries, detail: e.message });
  }
  throw e;
}

Prevention

When it happens

Trigger: Hand-written query strings like 'equal("status", archived)' (missing quotes, wrong casing, unsupported method); building strings by concatenation instead of the SDK's Query helpers; passing a single comma-joined string instead of an array of method(...) strings; using query methods that are not allowed on this endpoint.

Common situations: Porting curl examples into code and mangling quoting; switching SDK versions where helper output format changed; storing query strings in config and editing them by hand until they drift from the parser grammar.

Related errors


AI-assisted analysis of appwrite/appwrite@a1d520eea4 (2026-08-18). Data as JSON: /api/errors/a46b679e29ff965d. Report an issue: GitHub.