Skip to content

issues list/search may hit "Query too complex" at the default limit (CompleteIssueFields carries four unbounded connections) #286

Description

@iamfj

Summary

CompleteIssueFields carries four unbounded nested connections. Linear's complexity estimator charges an unbounded connection at its default page size (50) per parent row, so issues list and issues search — both defaulting to -l 50 — are priced the same way projects list was in #276, which #284 has just fixed for the projects domain only.

This is the same defect class as #276/#283, in a different (and more frequently used) domain.

Evidence

The unbounded connections in CompleteIssueFields (graphql/queries/issues.graphql):

  • labels (~line 29)
  • children (~line 50)
  • relations (~line 57)
  • inverseRelations

Consumers that spread it at the root with a user-controlled first:

  • GetIssues (graphql/queries/issues.graphql:235) ← issues list, default -l 50 (src/commands/issues.ts:604)
  • FilteredSearchIssues (graphql/queries/issues.graphql:357) ← issues search, default -l 50 (src/commands/issues.ts:638)

The arithmetic

#284 measured, against the live API, that each issue selected through CompleteIssueFields prices at ~260 (consistent with 4 connections × 50 rows × 1.3). That measurement is what justified lowering projects read --issues-first from 50 to 25 there — empirically it passes at 35 and fails at 40, i.e. a ceiling around 10000/263 ≈ 38 issues.

Applying the same model to issues list at its default limit of 50 gives ~13,000 against the 10,000 budget.

What needs checking first

The ~260 figure was measured for issues nested under a project, not for issues at the query root. Before any code change, someone with a real workspace should run:

linearis issues list
linearis issues search "<term>"
  • If they fail — this is a live P0-class bug on the most-used command in the tool, and the fix mirrors fix(projects): bound project query connections to avoid complexity limit #284: bound the four connections inside CompleteIssueFields. Note that fragment is shared far more widely than the project fragments were, so this is a cross-domain behaviour change (silent truncation of labels/children/relations per issue) and wants its own measurement pass rather than a copy of the 25s.
  • If they succeed — the estimator prices root connections differently from nested ones, and the cost model documented in graphql/queries/projects.graphql and src/services/project-service.ts should get a footnote saying so, since it currently reads as universal.

Either outcome is worth having written down.

Related, same shape

src/commands/issues.ts:1471 reads CompleteIssueFields.labels (unbounded, so an implicit 50) and writes the result back as a full-replacement labelIds. An issue with more than 50 labels would therefore lose labels 51+ on issues update --label-mode add. Unreachable in practice, but it is exactly the read-modify-write hazard #284 moved the projects domain off of (its lean pre-read now fetches 250), so the two domains are now inconsistent. Worth fixing in the same pass.

Context

Split out of the review of #284, which deliberately scoped itself to the projects domain. Nothing here regresses because of that PR — this defect predates it.

Refs #284
Refs #276
Refs #283

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions