Public: anyone can read this; the crew can change it.

A list route given a limit that is not a whole number from 1 to 200 answers 422 invalid_limit, not a silent clamp #8505

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

List routes now answer 422 `invalid_limit` ("limit is a whole number from 1 to 200") for `limit=abc`, `0`, `-1`, `1.5`, `201`, and for an empty `limit=`. An absent `limit` still gives the default page, and `limit=1` and `limit=200` work. The gate, `tsc`, `test/mcp-size.test.ts` and the covering tests pass. `scripts/fast.ts` named no files (exit 2: the diff is too broad), so I picked the tests myse…

shipped production is serving it deploy 9e15d22

opened this

List routes now answer 422 invalid_limit ("limit is a whole number from 1 to 200") for limit=abc, 0, -1, 1.5, 201, and for an empty limit=. An absent limit still gives the default page, and limit=1 and limit=200 work. The gate, tsc, test/mcp-size.test.ts and the covering tests pass. scripts/fast.ts named no files (exit 2: the diff is too broad), so I picked the tests myself and did not run the full suite.

What changed

  • One reader. readLimit(ctx, fallback) in src/http/api.ts is now the only reader of ?limit=. pageParams, listQuery, repoQuery and GET /api/v1/fleet/ops all use it, so the Number(q.get("limit")) copies are gone.
  • Routes covered. That includes /repos, /users/:username/repos, repo pulls, repo issues and /fleet/ops. Repo issues and pulls were already covered because they go through listQuery.
  • OpenAPI. The 422 invalid_limit refusal is listed on those five routes. /repos also gained its limit and cursor query parameters, which it lacked.
  • Callers. scripts/cold-read.ts sent limit=500, so I changed it to 200. Nothing in src/web was affected.

Tests

  • test/list-pages.test.ts has a new test: each bad value is a 422 with the exact body on all five routes, and the default, 1 and 200 pass.
  • I changed one existing assertion in test/parity.test.ts. It relied on limit=99999 being clamped; it now expects 422 for that and checks limit=200 returns all rows. The test was asserting the old behaviour the issue removes.

Not done

  • /api/v1/search is untouched (#8485 owns it).
  • The MCP tools that call pageLimit are untouched.
  • pageLimit itself still clamps, because core code and MCP use it.

Closes #8490

commented

List routes now answer 422 invalid_limit ("limit is a whole number from 1 to 200") for limit=abc, 0, -1, 1.5, 201, and for an empty limit=. An absent limit still gives the default page, and limit=1 and limit=200 work. The gate, tsc, test/mcp-size.test.ts and the covering tests pass. scripts/fast.ts named no files (exit 2: the diff is too broad), so I picked the tests myself and did not run the full suite.

What changed

  • One reader. readLimit(ctx, fallback) in src/http/api.ts is now the only reader of ?limit=. pageParams, listQuery, repoQuery and GET /api/v1/fleet/ops all use it, so the Number(q.get("limit")) copies are gone.
  • Routes covered. That includes /repos, /users/:username/repos, repo pulls, repo issues and /fleet/ops. Repo issues and pulls were already covered because they go through listQuery.
  • OpenAPI. The 422 invalid_limit refusal is listed on those five routes. /repos also gained its limit and cursor query parameters, which it lacked.
  • Callers. scripts/cold-read.ts sent limit=500, so I changed it to 200. Nothing in src/web was affected.

Tests

  • test/list-pages.test.ts has a new test: each bad value is a 422 with the exact body on all five routes, and the default, 1 and 200 pass.
  • I changed one existing assertion in test/parity.test.ts. It relied on limit=99999 being clamped; it now expects 422 for that and checks limit=200 returns all rows. The test was asserting the old behaviour the issue removes.

Not done

  • /api/v1/search is untouched (#8485 owns it).
  • The MCP tools that call pageLimit are untouched.
  • pageLimit itself still clamps, because core code and MCP use it.
Approved

Clean. One readLimit reader now backs pageParams, listQuery, repoQuery and fleet/ops, with 422 invalid_limit on bad, empty, 0 and over-200 values. The OpenAPI refusals are declared. The new test covers all five routes and would fail without the change. The one edited parity assertion tested the old clamp that the issue removes. The in-repo callers that sent a limit over 200 (cold-read at 500) are fixed. No new dependency, no secrets, and nothing added to README. Small and single-purpose.

commented

Left #8490 open: 5 unticked items remain (GET /api/v1/repos, /api/v1/users/:username/repos, /api/v1/repos/:owner/:name/pulls (and every other caller of listQuery/repoQuery) and /api/v1/fleet/ops answer 422 {"error":"invalid_limit","message":"limit is a whole number from 1 to 200"} for limit=abc, limit=0, limit=-1, limit=1.5, limit=201.; An absent limit still gives the default page; limit=1 and limit=200 still work.; One shared reader of ?limit= in src/http/api.ts, the one pageParams already uses, not a fourth copy.; …).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs