{"record":{"id":"7d1e6da244e89e81","repo":"paperclipai/paperclip","slug":"limit-must-be-a-number-between-1-and-500","errorCode":null,"errorMessage":"limit must be a number between 1 and 500","messagePattern":"limit must be a number between 1 and 500","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"server/src/routes/plugins.ts","lineNumber":2609,"sourceCode":"      return;\n    }\n\n    const { pluginId, jobId } = req.params;\n    const plugin = await resolvePlugin(registry, pluginId);\n    if (!plugin) {\n      res.status(404).json({ error: \"Plugin not found\" });\n      return;\n    }\n\n    const job = await jobDeps.jobStore.getJobByIdForPlugin(plugin.id, jobId);\n    if (!job) {\n      res.status(404).json({ error: \"Job not found\" });\n      return;\n    }\n\n    const limit = req.query.limit ? parseInt(req.query.limit as string, 10) : 25;\n    if (isNaN(limit) || limit < 1 || limit > 500) {\n      res.status(400).json({ error: \"limit must be a number between 1 and 500\" });\n      return;\n    }\n\n    try {\n      const runs = await jobDeps.jobStore.listRunsByJob(jobId, limit);\n      res.json(runs);\n    } catch (err) {\n      const message = err instanceof Error ? err.message : String(err);\n      res.status(500).json({ error: message });\n    }\n  });\n\n  /**\n   * POST /api/plugins/:pluginId/jobs/:jobId/trigger\n   *\n   * Manually trigger a job execution outside its cron schedule.\n   *\n   * Creates a run with `trigger: \"manual\"` and dispatches immediately.","sourceCodeStart":2591,"sourceCodeEnd":2627,"githubUrl":"https://github.com/paperclipai/paperclip/blob/a7e689b3c35347b529cb9f54c9b9a8575a3dcab6/server/src/routes/plugins.ts#L2591-L2627","documentation":"Returned as HTTP 400 by GET /api/plugins/:pluginId/jobs/:jobId/runs when the optional ?limit= query parameter parses to NaN, is below 1, or above 500. Default is 25 when omitted; any in-range integer 1..500 is accepted.","triggerScenarios":"Passing ?limit=0, ?limit=-1, ?limit=501, ?limit=abc, or ?limit=1e3. parseInt is used, so '50abc' passes but 'abc' fails; values outside 1..500 are rejected.","commonSituations":"UI pagination control allowing a page size beyond 500; passing a float like 10.5 (parseInt truncates to 10, which passes) or non-numeric input; copying a limit=1000 default from another endpoint with a higher cap.","solutions":["Clamp limit to an integer between 1 and 500 before sending","Omit ?limit to accept the default of 25","Sanitize user-supplied page sizes client-side (Math.min(500, Math.max(1, Math.floor(n))))"],"exampleFix":"// before\nGET /api/plugins/p/jobs/j/runs?limit=1000\n// after\nGET /api/plugins/p/jobs/j/runs?limit=100","handlingStrategy":"validation","validationCode":"function sanitizeLimit(raw: unknown, fallback = 25): number {\n  const n = Math.floor(Number(raw));\n  return Number.isFinite(n) ? Math.min(500, Math.max(1, n)) : fallback;\n}\nconst url = `/api/plugins/${pluginId}/jobs/${jobId}/runs?limit=${sanitizeLimit(userLimit)}`;","typeGuard":null,"tryCatchPattern":"try { await listJobRuns(pluginId, jobId, limit); } catch (e) { if (e.status === 400 && /limit/.test(e.body.error)) retryWithLimit(25); else throw e; }","preventionTips":["Clamp page sizes to 1..500 at the UI boundary","Cap pagination selectors at the API maximum rather than offering larger sizes","Send integers only — pre-round floats client-side"],"tags":["http-400","query-params","pagination","plugins","runs"],"backgroundTag":"invalid-query-parameter","analyzedSha":"a7e689b3c35347b529cb9f54c9b9a8575a3dcab6","analyzedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}