{"record":{"id":"b36eb6df7031f906","repo":"zylon-ai/private-gpt","slug":"offset-cannot-be-negative","errorCode":null,"errorMessage":"Offset cannot be negative","messagePattern":"Offset cannot be negative","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"private_gpt/components/web/web_search/providers/brave.py","lineNumber":96,"sourceCode":"\n    async def validate(self) -> None:\n        if not self._api_key or not self._api_key.strip():\n            raise ValueError(\"Brave Search API key is not configured\")\n\n    def _validate_query_params(\n        self, query: str, num_links: int, offset: int\n    ) -> tuple[str, int, int]:\n        if not query or not query.strip():\n            raise ValueError(\"Search query cannot be empty\")\n\n        normalized_num_links = max(1, min(20, num_links))  # Brave allows 1-20\n        if normalized_num_links != num_links:\n            logger.warning(\n                f\"Num_links {num_links} outside valid range [1,20], clamped to {normalized_num_links}\"\n            )\n\n        if offset < 0:\n            raise ValueError(\"Offset cannot be negative\")\n\n        return query, normalized_num_links, offset\n\n    async def _execute_http_request(\n        self,\n        query: str,\n        num_links: int,\n        offset: int,\n        result_filter: str,\n        safesearch: bool,\n        freshness: str | None,\n        spellcheck: bool,\n        language: str | None,\n    ) -> Any:\n        \"\"\"Execute HTTP request to Brave Search API.\n\n        This method performs the actual HTTP call without retry logic,\n        allowing for easier testing and mocking.","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/components/web/web_search/providers/brave.py#L78-L114","documentation":"ValueError from BraveSearchProvider._validate_query_params when the pagination offset is negative. Brave Search pagination uses offset >= 0; the provider validates this client-side (note that num_links out of range is only clamped with a warning, but a negative offset is a hard error).","triggerScenarios":"Passing offset=-1 via make_query kwargs (e.g. search(query, offset=page-1) with page=0); a pagination UI computing offset as (page-1)*n with page starting at 0 instead of 1.","commonSituations":"Off-by-one bugs in pagination loops; callers mixing 0-based page indices with Brave's page/offset semantics.","solutions":["Clamp offset at the call site: offset = max(0, desired_offset).","Fix the pagination arithmetic (use page-1 with pages starting at 1, or multiply 0-based index by page size).","Add a unit test asserting offsets never go negative for the first page."],"exampleFix":"# before\noffset = (page - 1) * page_size  # page is 0-based -> -page_size on first call\n\n# after\noffset = max(0, page) * page_size  # or page starting at 1","handlingStrategy":"validation","validationCode":"def normalize_offset(offset: int) -> int:\n    if offset < 0:\n        raise ValueError('offset must be >= 0')\n    return offset\n\noffset = normalize_offset((page - 1) * page_size if page >= 1 else 0)","typeGuard":null,"tryCatchPattern":"try:\n    results = await provider.make_query(q, n, offset=offset)\nexcept ValueError as e:\n    if str(e) == 'Offset cannot be negative':\n        results = await provider.make_query(q, n, offset=0)\n    else:\n        raise","preventionTips":["Use max(0, offset) at pagination call sites.","Define page indices as 1-based in your API and convert once.","Property-test pagination bounds in CI."],"tags":["validation","pagination","brave","off-by-one"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}