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

  1. Use the bookmark object returned by the previous fetch response (response.bookmark) verbatim rather than a string.
  2. If you have a string bookmark, convert/round-trip it through the API that produced it, or start a fresh search with no bookmark.
  3. Update client code/types so bookmark is typed as the structured SearchFilters-compatible bookmark, not string.
  4. 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

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


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/1721e44526f8a6a9. Report an issue: GitHub.