Budibase/budibase · error
String based bookmark not supported.
Error message
String based bookmark not supported.
What it means
Audit log search in Budibase Pro accepts pagination bookmarks as structured objects, not raw strings. When a caller passes a string bookmark (e.g. a CouchDB-style pagination token) into fetch, it throws this error instead of attempting an invalid search. It signals the caller is using the wrong bookmark format for this API.
Source
Thrown at packages/pro/src/sdk/auditLogs/auditLogs.ts:121
}
if (log.appId) {
enrichedLog.app =
workspace || deleted(log.appId, ResourceType.APP, log.fallback)
}
enriched.push(enrichedLog)
}
return enriched
}
export async function fetch(params: AuditLogSearchParams) {
if (!(await isAuditLogsEnabled())) {
throw new Error("Audit logs not available - license required.")
}
const filter: SearchFilters = await getSearchFilters(params)
if (typeof params.bookmark === "string") {
throw new Error("String based bookmark not supported.")
}
const response = await auditLogs.searchSQL(filter, params.bookmark)
return {
hasNextPage: response.hasNextPage,
bookmark: response.bookmark,
data: await enrich(response.rows),
}
}
// like the fetch, but without pagination (uses filtered replication)
export function download(params: AuditLogSearchParams): {
promise: Promise<void>
stream: Readable
} {
params = fillDates(params)
return auditLogs.dump(params)
}View on GitHub (pinned to a81a902e9a)
Solutions
- Use the bookmark object returned by the previous fetch response (response.bookmark) verbatim rather than a string.
- If you have a string bookmark, convert/round-trip it through the API that produced it, or start a fresh search with no bookmark.
- Update client code/types so bookmark is typed as the structured SearchFilters-compatible bookmark, not string.
- Ensure the tenant has the audit logs license; if you fell back to a legacy string-bookmark path due to licensing, upgrade the license.
Example fix
// before
await auditLogs.fetch({ bookmark: "eyJpZCI6..." })
// after
const first = await auditLogs.fetch({})
await auditLogs.fetch({ bookmark: first.bookmark }) Defensive patterns
Strategy: validation
Validate before calling
if (typeof params.bookmark === "string") {
throw new TypeError("bookmark must be the structured object from a previous fetch response")
} Type guard
function isStructuredBookmark(b: unknown): b is Record<string, unknown> {
return typeof b === "object" && b !== null && !Array.isArray(b)
} Prevention
- Always pass through the bookmark object returned by the previous response untouched
- Never serialize/deserialize the bookmark to a plain string between calls
- Type the params so bookmark cannot be a string in your client code
When it happens
Trigger: Calling fetch (auditLogs.fetch) with params.bookmark set to a string value, e.g. passing a bookmark string returned by a different search API or copied from a URL query parameter instead of the object-shaped bookmark returned by this API's previous response.
Common situations: Migrating code from the older string-based pagination API to the SQL-backed audit log search; hand-crafting pagination requests from the builder HTTP API where bookmark was serialized to a string; copying a bookmark from server logs.
Related errors
- Error getting status
- Could not refresh OAuth Token
- Invitation is not valid or has expired, please request a new
- Audit logs not available - license required.
- Invalid bookmark query
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/1721e44526f8a6a9.
Report an issue: GitHub.