Widgets run a SQL query and return the result to your browser. Two hard limits apply to every widget query. If a query goes over either limit, the widget fails to load and shows an error. This guide explains the limits, what causes a query to hit them, and how to keep your widgets under the limits.
Result payload cap — 10 MB. The result that a widget query returns must be smaller than 10,485,760 bytes (10 MB). If the result is larger, the widget fails with this error:
rpc error: code = ResourceExhausted desc = grpc: received message larger than maxThe number in the error is the size of your result in bytes, compared to the 10 MB cap. A result of many un-aggregated rows is the common cause.
Query memory cap — 4 GB. The query engine has 4 GB of memory to run a widget query. If the query needs more memory than this while it runs, the widget fails with this error:
Memory limit exceeded — The widget is consuming too much memory.This limit is about how the query runs, not how large the result is. A query can return a small result and still go over the memory cap while it computes that result.
Five patterns cause most failures:
Wide-table JOINs. A wide table (for example, an issue table with several hundred columns) placed on the right side of a JOIN forces the engine to load every column into memory, even columns you do not select. This is the most common cause of the memory error.
Un-aggregated, unbounded result sets. A query that returns one row per raw record, with no aggregation and no LIMIT, can return tens of thousands of rows. This is the most common cause of the payload error.
Too many columns. Each extra column adds to both the memory used and the result size.
High-cardinality GROUP BYs. A GROUP BY on a field with many distinct values produces many result rows.
No scope filter. A query that scans a whole dataset, with no date, team, or account filter, reads far more data than a widget needs.
Apply these rules when you write or edit a widget query.
Larger table on the left of a JOIN. The engine loads all columns of the right-side table into memory. Put the larger or wider table on the left, and the smaller table on the right. This one change can cut memory use by several times.
Only necessary columns. Do not select columns the widget does not display. Each column adds memory and payload. List the exact columns instead of selecting all of them.
Aggregation before rows. Return one row per group, not one row per raw record. Use SUM, COUNT, AVG, or a similar function to reduce the row count. Aggregate in the query, not in the widget.
Scope filter. Filter to a date range, a team, a sprint, or an account. A widget shows a bounded view, so the query does not need to scan the whole dataset.
LIMIT on detail queries. If a widget shows a list of records, add a LIMIT to the final result. This caps the payload even if a filter is missing.
Split of a heavy widget. If one widget needs many columns or many metrics, divide it. Several narrow widgets each stay under the limits. One wide widget does not.
Pre-aggregation into a dataset. If a heavy query runs on every load, compute the aggregated result once into a dataset. Point the widget at that dataset. The widget then reads a small, ready result.
A widget query reconstructs the full stage-change history for every issue. It returns one row per stage change, with no aggregation and no limit. The result is about 22.5 MB, which is more than twice the 10 MB cap, so the widget fails with ResourceExhausted.
Before — fails. The query:
Scans the whole issue dataset.
Returns one raw row per stage transition per issue.
Has no aggregation and no LIMIT.
Includes wide, unused columns.
After — compliant. Change the query to:
Aggregate to one row per issue per stage, with SUM(business_minutes) for the time in each stage.
Filter to one team, sprint, or date window instead of the whole dataset.
Add a LIMIT to the final result.
Remove wide, unused columns.
The result drops from tens of thousands of raw rows to a small aggregated set, well under both caps.
A separate memory case shows the same effect on the JOIN pattern. One production widget used 6,550 MB of memory and failed. Moving the wide table to the left of the JOIN and pruning unused columns dropped it to 1,440 MB, under the 4 GB cap.
Run this checklist before you save any widget:
Is the larger or wider table on the left of every JOIN?
Do you select only the columns the widget displays?
Do you aggregate the result, instead of returning raw rows?
Is there a scope filter (date, team, sprint, or account)?
Does a detail (list) widget have a LIMIT?
Could you split a heavy widget into several narrow widgets?
If a widget still fails after these steps, pre-aggregate the data into a dataset and point the widget at it.
Error text | Limit hit | Most likely cause |
|---|---|---|
| 10 MB result payload | Un-aggregated, unbounded result; too many rows or columns |
| 4 GB query memory | Wide table on the right of a JOIN; unbounded scan |