RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-query

error-invalid-query

Error message

isValidQuery.errors.join('\n')

What it means

Thrown by the users.list query path when the sanitized query fails validateQuery (the ajv-based guard that restricts allowed fields/operators). The message is the joined ajv error list, so it tells you exactly which field/operator was rejected.

Source

Thrown at apps/meteor/server/api/v1/users.ts:707

			// if user provided a query, validate it with their allowed operators
			// otherwise we use the default query (with $regex and $options)
			if (
				!isValidQuery(
					nonEmptyQuery,
					[
						...inclusiveFieldsKeys,
						inclusiveFieldsKeys.includes('emails') && 'emails.address.*',
						inclusiveFieldsKeys.includes('username') && 'username.*',
						inclusiveFieldsKeys.includes('name') && 'name.*',
						inclusiveFieldsKeys.includes('type') && 'type.*',
						inclusiveFieldsKeys.includes('customFields') && 'customFields.*',
					].filter(Boolean) as string[],
					// At this point, we have already validated the user query not containing malicious fields
					// On here we are using our own query so we can allow some extra fields
					[...this.queryOperations, '$regex', '$options'],
				)
			) {
				throw new Meteor.Error('error-invalid-query', isValidQuery.errors.join('\n'));
			}

			const actualSort = sort || { username: 1 };

			if (sort?.status) {
				actualSort.active = sort.status;
			}

			if (sort?.name) {
				actualSort.nameInsensitive = sort.name;
			}

			const limit =
				count !== 0
					? [
							{
								$limit: count,
							},

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Read the joined error text returned - it names the offending field/operator; remove or replace it.
  2. Restrict the query to the documented allowed fields and the allowed operator set ([queryOperations, $regex, $options]).
  3. URL-encode and JSON-validate the query parameter before sending.

Example fix

// before
GET /api/v1/users.list?query={ "services.google": { $exists: true } }

// after - query an allowed, indexed field
GET /api/v1/users.list?query={ "emails.address": { $regex: "@example.com", $options: "i" } }
Defensive patterns

Strategy: validation

Validate before calling

// Allow-list fields and operators before sending the query
const ALLOWED_FIELDS = ['username', 'emails.address', 'name', 'type', 'status', 'roles'];
const ALLOWED_OPS = ['$eq', '$ne', '$in', '$nin', '$regex', '$options', '$exists'];
function sanitizeQuery(q: unknown): unknown {
  // walk q, drop unknown keys/ops, return cleaned query or null
}
const clean = sanitizeQuery(rawQuery);
await GET(`users.list?query=${encodeURIComponent(JSON.stringify(clean))}`);

Type guard

const isAllowedQuery = (q: any, allowed: string[]): boolean =>
  q && typeof q === 'object' && Object.keys(q).every((k) => allowed.includes(k));

Try / catch

try {
  await GET(`users.list?query=${encodeURIComponent(JSON.stringify(q))}`);
} catch (e) {
  if (isMeteorError(e, 'error-invalid-query')) {
    // e.reason contains the ajv errors; use them to fix the query
  } else { throw e; }
}

Prevention

When it happens

Trigger: GET users.list?query=... with a field or MongoDB operator not in the allowed allow-list, or with a syntactically invalid query JSON.

Common situations: Client builds a query with a field the server does not expose (e.g. arbitrary services.* keys); using $where or other banned operators; malformed JSON from a query builder.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/0ff3a8034c44cd92. Report an issue: GitHub.