{"record":{"id":"aaec56857ad6a6b4","repo":"Budibase/budibase","slug":"invalid-bookmark-query","errorCode":null,"errorMessage":"Invalid bookmark query","messagePattern":"Invalid bookmark query","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"warning","filePath":"packages/server/src/api/controllers/ai/agentLogs.ts","lineNumber":30,"sourceCode":"\nfunction getDefaultLogRange() {\n  const now = new Date()\n  const sevenDaysAgo = new Date(now.getTime() - Duration.fromDays(7).toMs())\n\n  return {\n    startDate: sevenDaysAgo.toISOString(),\n    endDate: now.toISOString(),\n  }\n}\n\nfunction sanitizeBookmarkQuery(bookmark?: string): string | undefined {\n  const normalizedBookmark = bookmark?.trim()\n  if (!normalizedBookmark) {\n    return undefined\n  }\n\n  if (!/^\\d+$/.test(normalizedBookmark)) {\n    throw new HTTPError(\"Invalid bookmark query\", 400)\n  }\n\n  const parsedBookmark = Number.parseInt(normalizedBookmark, 10)\n  if (!Number.isFinite(parsedBookmark) || parsedBookmark < 1) {\n    throw new HTTPError(\"Invalid bookmark query\", 400)\n  }\n\n  return String(parsedBookmark)\n}\n\nfunction sanitizeLimitQuery(limit?: string): number | undefined {\n  const normalizedLimit = limit?.trim()\n  if (!normalizedLimit) {\n    return undefined\n  }\n\n  if (!/^\\d+$/.test(normalizedLimit)) {\n    throw new HTTPError(\"Invalid limit query\", 400)","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/ai/agentLogs.ts#L12-L48","documentation":"sanitizeBookmarkQuery validates the pagination bookmark query parameter and throws HTTPError 400 when it is not a pure numeric string. Bookmarks for agent logs are integer-based cursors, so any non-numeric value is rejected before querying.","triggerScenarios":"GET agent logs with ?bookmark=abc, ?bookmark=12ab, or whitespace-only/string values failing the /^\\d+$/ test.","commonSituations":"Client passing an opaque/offset cursor from another API; hand-crafted URL with malformed bookmark; passing undefined-turned-empty handled (returns undefined) but non-numeric strings rejected.","solutions":["Send only digits as the bookmark value","Use the bookmark value returned by the previous agent-logs response verbatim","Remove the bookmark param to fetch the first page"],"exampleFix":"// before\nfetch(\"/api/ai/agent/logs?bookmark=page-2\")\n// after\nfetch(\"/api/ai/agent/logs?bookmark=20\")","handlingStrategy":"validation","validationCode":"const bookmark = rawBookmark?.trim(); if (bookmark && !/^\\d+$/.test(bookmark)) throw new Error(\"bookmark must be numeric\")","typeGuard":"const isValidBookmark = (v: unknown): v is string => typeof v === \"string\" && /^\\d+$/.test(v.trim())","tryCatchPattern":"try { await api.fetchAgentLogs({ bookmark }) } catch (e) { if (e.status === 400 && e.message === \"Invalid bookmark query\") { /* reset to first page */ } else throw e }","preventionTips":["Only send bookmark values returned by previous responses","Omit bookmark for the first page","Client-side validate cursors before sending"],"tags":["validation","pagination","http-400"],"backgroundTag":"invalid-query-parameter","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}