zylon-ai/private-gpt · warning · ValueError
Invalid page token
Error message
Invalid page token
What it means
Pagination in the skill repository uses a plain integer page number as the token (see new_version_token returning epoch milliseconds); _parse_page converts the token with int() and rejects negative values with ValueError('Invalid page token'). Note that int(page) itself will raise ValueError on non-numeric strings before the negativity check — same message space, different trigger — so any malformed token surfaces as this error.
Source
Thrown at private_gpt/components/skills/repositories/skill_repository.py:447
def new_skill_id() -> str:
return f"skill_{uuid.uuid4().hex}"
def new_skill_version_id() -> str:
return f"skillver_{uuid.uuid4().hex}"
def new_version_token() -> str:
return str(time.time_ns() // 1_000)
def _parse_page(page: str | None) -> int:
if page is None:
return 0
value = int(page)
if value < 0:
raise ValueError("Invalid page token")
return value
def _skill_from_orm(
row: SkillORM,
latest_version: str | None,
) -> SkillEntity:
source = cast(Literal["custom", "anthropic", "zylon"], row.source)
loading = cast(Literal["eager", "lazy"], row.loading)
return SkillEntity(
id=row.id,
collection=row.collection,
display_title=row.display_title,
source=source,
loading=loading,
readonly=row.readonly,
latest_version=latest_version,
created_at=row.created_at,View on GitHub (pinned to 4a030776a3)
Solutions
- Echo the token received from the previous response verbatim; do not encode/decode or transform it.
- For the first page, omit the parameter entirely (page=None maps to 0) rather than sending an empty string.
- Sanitize input: `_t = token if token and token.isdigit() else None` before calling the API.
- If you generate tokens yourself, use new_version_token()/new_skill_token() so the format matches.
Example fix
# before
items = await repo.list_skills(collection, page=request.query_params.get("page", ""))
# ValueError: Invalid page token
# after
raw = request.query_params.get("page")
token = raw if raw and raw.isdigit() else None
items = await repo.list_skills(collection, page=token) Defensive patterns
Strategy: validation
Validate before calling
def safe_page_token(raw: str | None) -> str | None:
if raw is None or raw == "":
return None
if not raw.isdigit():
raise HTTPException(400, "page must be a non-negative integer")
return raw Type guard
def is_valid_page_token(value: str | None) -> bool:
return value is None or (value.isdigit() and int(value) >= 0) Try / catch
try:
page = repo._parse_page(raw) # or the public list call
except ValueError:
raw = None # reset to first page
page = repo._parse_page(raw) Prevention
- Pass tokens through verbatim; never re-encode or trim them.
- Use None (omitted param) for the first page instead of an empty string.
- Validate query params with isdigit() at the API boundary.
When it happens
Trigger: Passing a page token that is not a non-negative integer string — e.g. 'abc', '-1', '1.5', an opaque cursor from another pagination scheme, or None handling that forwards an empty string.
Common situations: Clients treating the token as an opaque cursor and mangling it (base64, JSON-encoding); forwarding tokens from a different API; corrupt query parameters; defaulting a missing param to '' instead of None.
Related errors
- Provide SKILL.md either in files or skill_md
- Invalid tool specification: {tool}
- 'oneOf' must be an array of schemas
- 'anyOf' must be an array of schemas
- 'allOf' must be an array of schemas
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/8a0e8a1a8c25bf60.
Report an issue: GitHub.