Reproducer
Active cycle with 264 open tickets. The CLI returns 50.
$ linear tasks --all --status open --json | jq '.issues | length'
50
Same cycle, paginated raw GraphQL (first: 100 + cursor loop on pageInfo.hasNextPage):
Root cause
linear line 621–626, inside list_tasks:
issues(filter: {
team: { id: { eq: "{team_id}" }}
{filter_str}
}) {
nodes { {ISSUE_FIELDS} }
}
No first: arg, no pageInfo { hasNextPage endCursor }, no cursor loop. Linear's GraphQL API defaults paginated connections to 50 nodes per page when first: is omitted — so the CLI silently truncates at 50 every time.
Same pattern in show_board at line 696 area (same query shape).
For comparison, the existing list_team_cycles helper at line 284 uses first: 50 explicitly but still doesn't loop on pageInfo. The user-list path at line 935 (first: 200) and the projects path at line 1125 (first: 200) are also single-shot.
Why it's bad
This silently produces wrong answers. A subagent (or human) running linear tasks on a busy team sees the first 50 and assumes that's the whole queue. Triage decisions get made on incomplete data; backlog cleanup misses 75% of the work; "how many open tickets do I have" returns numbers that don't match reality.
The promise of `linear tasks --json | jq` is that the JSON is the answer — when it's a quietly truncated subset, the abstraction leaks in the worst possible way.
Fix
Cursor loop on pageInfo. Shape:
def fetch_all(query_template, page_size=100):
all_nodes = []
cursor = None
while True:
q = query_template(first=page_size, after=cursor)
data = gql(api_key, q)
page = data[...]['issues'] # or whichever connection
all_nodes.extend(page['nodes'])
if not page['pageInfo']['hasNextPage']:
break
cursor = page['pageInfo']['endCursor']
return all_nodes
Apply at every connection query: list_tasks (line 621), show_board (line 696), list_team_cycles (line 284), users (line 935), projects (line 1125), and any others I missed. A small `paginate()` helper would avoid copy-paste.
Optional: keep an opt-out (`--limit 50`) for fast surveys, but the default should be complete.
Impact rating
P0. This bug means `linear tasks --json` returns wrong answers on any team with >50 open issues in active cycle. Affects every workflow built on top of the CLI.
Reproducer
Active cycle with 264 open tickets. The CLI returns 50.
Same cycle, paginated raw GraphQL (
first: 100+ cursor loop onpageInfo.hasNextPage):Root cause
linearline 621–626, insidelist_tasks:No
first:arg, nopageInfo { hasNextPage endCursor }, no cursor loop. Linear's GraphQL API defaults paginated connections to 50 nodes per page whenfirst:is omitted — so the CLI silently truncates at 50 every time.Same pattern in
show_boardat line 696 area (same query shape).For comparison, the existing list_team_cycles helper at line 284 uses
first: 50explicitly but still doesn't loop onpageInfo. The user-list path at line 935 (first: 200) and the projects path at line 1125 (first: 200) are also single-shot.Why it's bad
This silently produces wrong answers. A subagent (or human) running
linear taskson a busy team sees the first 50 and assumes that's the whole queue. Triage decisions get made on incomplete data; backlog cleanup misses 75% of the work; "how many open tickets do I have" returns numbers that don't match reality.The promise of `linear tasks --json | jq` is that the JSON is the answer — when it's a quietly truncated subset, the abstraction leaks in the worst possible way.
Fix
Cursor loop on
pageInfo. Shape:Apply at every connection query: list_tasks (line 621), show_board (line 696), list_team_cycles (line 284), users (line 935), projects (line 1125), and any others I missed. A small `paginate()` helper would avoid copy-paste.
Optional: keep an opt-out (`--limit 50`) for fast surveys, but the default should be complete.
Impact rating
P0. This bug means `linear tasks --json` returns wrong answers on any team with >50 open issues in active cycle. Affects every workflow built on top of the CLI.