Docs/Error messages/Resources exceeded: allotted memory
Error messagesResources

Resources exceeded during query execution: The query could not be executed in the allotted memory

A single worker ran out of memory because of the shape of the query, which has nothing to do with how many bytes it reads. The text after the colon names the stage, and ORDER BY is the usual one.

Resources exceeded during query execution: The query could not be executed in the allotted memory. Peak usage: PERCENTAGE% of limit. Top memory consumer(s): ORDER BY operations.

What BigQuery means

One worker ran out of memory. One worker was handed more data than it could hold, because of how the query is shaped. The project and the reservation are not involved.

This has nothing to do with how much the query costs or how many bytes it scans. A query that reads 50 GB can fail this way while a query that reads 5 TB succeeds. What matters is whether any single stage has to hold a large amount of data in one place.

Read the text after the colon. It names the stage.

Top memory consumer: ORDER BY operations

The common case by a wide margin. A global ORDER BY has to see every row to decide the order, and BigQuery does that sort on one compute unit.

SELECT title
FROM `my-project.mydataset.mytable`
ORDER BY title

With millions of distinct values in title, this fails.

Add a LIMIT. With a limit, BigQuery only has to keep that many rows:

SELECT title
FROM `my-project.mydataset.mytable`
ORDER BY title DESC
LIMIT 1000

Drop the ORDER BY if the result goes to a table. Sorting a destination table buys nothing. BigQuery does not keep the order and a later reader will not see it.

Do not sort in the middle of a query. An ORDER BY inside a subquery or a CTE is work that the outer query throws away. Keep it in the outermost query, or inside a window function’s OVER clause where it belongs.

Replace a large OFFSET with ROW_NUMBER(). ORDER BY ... LIMIT 10 OFFSET 1000000 makes the sort stage process a million rows before it can discard them. Google’s own suggestion is to rank instead and filter the rank:

SELECT *
FROM (
  SELECT *, ROW_NUMBER() OVER (ORDER BY created_at) AS rn
  FROM `my-project.mydataset.mytable`
)
WHERE rn > @start_index AND rn <= @page_size + @start_index

ROW_NUMBER() starts at 1.

The other variants

Each one has its own text and its own fix.

Query exceeded resource limits

Query exceeded resource limits

An on-demand query that used a lot of CPU relative to the bytes it scanned. Usually heavy per-row work: regular expressions, JSON parsing, user-defined functions over every row. Push the expensive expressions to the end of the query so they run on fewer rows.

Out of stack space

Out of stack space due to deeply nested query expression during query resolution.

Too many nested function calls. This bites people who build an expression in a loop, because A || B || C || ... becomes CONCAT(A, CONCAT(B, CONCAT(C, ...))) during parsing. Rewrite with ARRAY_TO_STRING over an array, or split the expression.

Query exceeds shuffle resources

Resources exceeded during query execution: Your project or organization exceeded the maximum disk and memory limit available for shuffle operations

Too much data moved between stages. Usually a join that multiplies rows, or a join key with heavy skew so one worker gets most of the rows.

This is the one variant that also responds to capacity. Google suggests more slots or less data. On-demand pricing caps you at 2,000 slots per project, so going above that means moving to capacity pricing. Filtering and aggregating before the join is usually cheaper than buying slots. See Optimizing joins.

Query is too complex

Resources exceeded during query execution: Not enough resources for query planning - too many subqueries or query is too complex

The planner gave up, before anything ran. The causes Google names are deeply nested or repeatedly used WITH clauses, views stacked on views, and repeated UNION ALL.

A WITH clause is not a materialized result. BigQuery may recompute it every time it is referenced, so a CTE used five times can be five full computations. Writing the intermediate result to a temporary table and selecting from that is the standard fix, and it is also the fix for the slow version of the same query that does not fail outright.

What does not fix it

  • Raising maximum_bytes_billed. That is a different limit, with a different message.
  • Retrying. The memory variant is deterministic: the same query on the same data fails again. The shuffle variant depends on what else is running, so a retry sometimes gets through, which is luck rather than a fix.
  • Buying slots, except for the shuffle variant.

The execution graph tells you which stage failed, which is the fastest way to confirm you are fixing the right part of the query.

Finding out before it fails

BigQuery writes a warning into INFORMATION_SCHEMA.JOBS for queries that are getting close to the complexity limit, before they start failing:

SELECT
  ANY_VALUE(query) AS query,
  MAX(query_info.resource_warning) AS resource_warning
FROM `my-project.region-us`.INFORMATION_SCHEMA.JOBS
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 3 DAY)
  AND query_info.resource_warning IS NOT NULL
GROUP BY query_info.query_hashes.normalized_literals
LIMIT 1000

The job’s error_result.reason is resourcesExceeded.

In Querylab.io

Querylab.io warns on an ORDER BY with no LIMIT over a large table, the case above, and offers an Add LIMIT quick fix (code quality).