{"record":{"id":"3e3985844bb1259f","repo":"gastownhall/beads","slug":"query-pagination-exceeded-d-pages","errorCode":null,"errorMessage":"query pagination exceeded %d pages","messagePattern":"query pagination exceeded (.+?) pages","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/notion/client.go","lineNumber":146,"sourceCode":"\t\tif cursor != \"\" {\n\t\t\trequest[\"start_cursor\"] = cursor\n\t\t}\n\n\t\tbody, err := c.doRequest(ctx, http.MethodPost, \"/data_sources/\"+url.PathEscape(dataSourceID)+\"/query\", request)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvar resp QueryDataSourceResponse\n\t\tif err := json.Unmarshal(body, &resp); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"parse data source query response: %w\", err)\n\t\t}\n\t\tpages = append(pages, resp.Results...)\n\t\tif !resp.HasMore || resp.NextCursor == \"\" {\n\t\t\treturn pages, nil\n\t\t}\n\t\tcursor = resp.NextCursor\n\t}\n\treturn nil, fmt.Errorf(\"query pagination exceeded %d pages\", maxQueryPages)\n}\n\nfunc (c *Client) CreatePage(ctx context.Context, dataSourceID string, properties map[string]interface{}) (*Page, error) {\n\trequest := map[string]interface{}{\n\t\t\"parent\": map[string]interface{}{\n\t\t\t\"type\":           \"data_source_id\",\n\t\t\t\"data_source_id\": dataSourceID,\n\t\t},\n\t\t\"properties\": properties,\n\t}\n\tbody, err := c.doRequest(ctx, http.MethodPost, \"/pages\", request)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar page Page\n\tif err := json.Unmarshal(body, &page); err != nil {\n\t\treturn nil, fmt.Errorf(\"parse create page response: %w\", err)\n\t}","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/notion/client.go#L128-L164","documentation":"QueryDataSource follows next_cursor pagination up to maxQueryPages iterations; if has_more remains true past that bound it returns this error instead of looping forever. It is a safety guard against runaway or malformed pagination from the API.","triggerScenarios":"Calling QueryDataSource on a data source whose result set requires more pages than maxQueryPages, or a stuck cursor where the API keeps returning has_more=true with the same/next cursor indefinitely.","commonSituations":"Querying a very large Notion database (tens of thousands of rows) with a small page_size; a broken cursor loop caused by an API bug or by repeatedly sending the same cursor due to proxy caching.","solutions":["Narrow the query with a filter or smaller property set so fewer pages are needed.","Increase the maxQueryPages constant if the data set legitimately exceeds the bound.","Implement manual pagination yourself using resp.NextCursor with an unbounded loop and your own safety limit.","Check for proxy caching that returns the same page repeatedly (cursor never advances)."],"exampleFix":"// before\npages, err := client.QueryDataSource(ctx, dsID, nil)\n// after\npages, err := client.QueryDataSource(ctx, dsID, map[string]interface{}{\n    \"filter\": map[string]interface{}{\"property\": \"Status\", \"select\": {\"equals\": \"Open\"}},\n    \"page_size\": 100,\n})","handlingStrategy":"validation","validationCode":"if len(filter) == 0 {\n    // estimate result count first or require a filter before querying large sources\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass a filter to QueryDataSource on large data sources","Raise page_size (up to 100) to reduce page count","Know your data source size before unbounded queries","Increase maxQueryPages deliberately if you expect deep pagination"],"tags":["notion","pagination","api","limit-exceeded"],"backgroundTag":"pagination-limit-exceeded","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}