Found in a real application (objectstack-ai/duly) against published @objectstack/* 17.2.0, measured in a browser on a seeded store of 186 rows. Filed unassigned. Two related observations from one page load; they share a cause and I would rather they be judged together than split.
Distinct from objectui#7189, which is plugin-grid's useGroupedData grouping over a fetched page. This is the opposite problem on a different surface: the gantt is not page-scoped, and the paging chrome above it says otherwise.
Measured
One load of a type: 'gantt' list view (groupByField: 'owner', a two-rule is_not_null filter, sort: visible_from asc, no pagination block authored) issues both of these:
GET /api/v1/data/duly_task?populate=owner&top=100&select=id,subject,status,owner,visible_from,due_date
&sort=visible_from&filter=[["visible_from","is_not_null"],["due_date","is_not_null"]]
→ 100 rows · total 186 · hasMore true
GET /api/v1/data/duly_task?sort=visible_from
&filter=["and",["visible_from","isnotnull",null],["due_date","isnotnull",null]]
→ 186 rows · total 186 · hasMore false
Note they do not even agree on a filter dialect — the paged one sends the authored rule array, the unbounded one sends a compiled ["and", …] tree.
The chart draws the second one. Counted off the DOM rather than inferred: 186 gantt-task-row-<id> rows plus 12 gantt-task-row-__group__<id> rows, and the virtualised task list's scrollHeight is 7920px = 198 rows × 40px. The bars span the full 2026-01-31 → 2026-12-31 range of the whole result set, six months past the 100-row page's own max(due_date) of 2026-06-30.
The footer reports the first one, in the paging chrome under the chart:
100 records · Showing first 100 records. More data may be available.
Why each half is worth something
1. The footer is wrong about the thing it sits under. It is the only paging disclosure on the screen, and a reader takes it as describing the chart. Here it warned that a complete chart might be partial. The same mechanism inverts the moment anything caps the second request: the footer would then say 100 records while the chart drew some other number, and nothing on screen would reconcile them. Either way it is not a statement about the chart.
That reading is not hypothetical — it is what happened. A card was filed in the application repo (objectstack-ai/duly#96) concluding from this footer that the gantt's timeline range and owner grouping were computed over a 100-row slice. The reasoning was sound given what the screen said; it just was not what the code does. It cost a browser session to disprove.
2. The fetch that matters has no ceiling.@objectstack/rest applies no cap when limit is absent, so the unbounded request returns the entire filtered result set. At 186 rows that is invisible. On a task object with 100k scheduled rows it is the whole table into the browser, with no page-size knob reachable from the view metadata — authoring pagination.pageSize cannot bound it, because this request never carried a top to begin with.
The two halves pull in opposite directions, which is the actual design question: whichever way it is settled, the footer and the chart should be describing the same fetch.
Suggested direction
- Make the paging chrome describe the fetch the visualisation actually consumed — or suppress it on surfaces where it does not apply. A footer that reports a request the user is not looking at is worse than no footer.
- Decide whether a non-grid view is allowed an unbounded fetch, and if it is, say so at author time. A gantt does need enough rows to compute a truthful range, which is a real argument against naive paging here — but "unbounded, silently" should be a decision rather than the absence of a
top. - Not two requests. Whatever the answer to (2), the same rows are being fetched twice per load under two different filter dialects.
Related, same component, both closed: objectui#6419 and objectui#6460 are previous findings on this ObjectView non-grid fetch path.
Application impact
None today, and no workaround is in place for it — duly is scoping that lens to open work for a product reason (a schedule of finished work is not what the screen is for), which incidentally puts the result set at 33 rows and makes both requests agree. That is our data size, not a fix; nothing in an application can bound the unbounded request or correct the footer.
Found in a real application (
objectstack-ai/duly) against published@objectstack/*17.2.0, measured in a browser on a seeded store of 186 rows. Filed unassigned. Two related observations from one page load; they share a cause and I would rather they be judged together than split.Distinct from objectui#7189, which is
plugin-grid'suseGroupedDatagrouping over a fetched page. This is the opposite problem on a different surface: the gantt is not page-scoped, and the paging chrome above it says otherwise.Measured
One load of a
type: 'gantt'list view (groupByField: 'owner', a two-ruleis_not_nullfilter,sort: visible_from asc, nopaginationblock authored) issues both of these:Note they do not even agree on a filter dialect — the paged one sends the authored rule array, the unbounded one sends a compiled
["and", …]tree.The chart draws the second one. Counted off the DOM rather than inferred: 186
gantt-task-row-<id>rows plus 12gantt-task-row-__group__<id>rows, and the virtualised task list'sscrollHeightis 7920px = 198 rows × 40px. The bars span the full2026-01-31 → 2026-12-31range of the whole result set, six months past the 100-row page's ownmax(due_date)of2026-06-30.The footer reports the first one, in the paging chrome under the chart:
Why each half is worth something
1. The footer is wrong about the thing it sits under. It is the only paging disclosure on the screen, and a reader takes it as describing the chart. Here it warned that a complete chart might be partial. The same mechanism inverts the moment anything caps the second request: the footer would then say
100 recordswhile the chart drew some other number, and nothing on screen would reconcile them. Either way it is not a statement about the chart.That reading is not hypothetical — it is what happened. A card was filed in the application repo (
objectstack-ai/duly#96) concluding from this footer that the gantt's timeline range and owner grouping were computed over a 100-row slice. The reasoning was sound given what the screen said; it just was not what the code does. It cost a browser session to disprove.2. The fetch that matters has no ceiling.
@objectstack/restapplies no cap whenlimitis absent, so the unbounded request returns the entire filtered result set. At 186 rows that is invisible. On a task object with 100k scheduled rows it is the whole table into the browser, with no page-size knob reachable from the view metadata — authoringpagination.pageSizecannot bound it, because this request never carried atopto begin with.The two halves pull in opposite directions, which is the actual design question: whichever way it is settled, the footer and the chart should be describing the same fetch.
Suggested direction
top.Related, same component, both closed: objectui#6419 and objectui#6460 are previous findings on this
ObjectViewnon-grid fetch path.Application impact
None today, and no workaround is in place for it —
dulyis scoping that lens to open work for a product reason (a schedule of finished work is not what the screen is for), which incidentally puts the result set at 33 rows and makes both requests agree. That is our data size, not a fix; nothing in an application can bound the unbounded request or correct the footer.