{"record":{"id":"9599635783883c51","repo":"ory/hydra","slug":"plan-must-define-a-defaultpagetoken","errorCode":null,"errorMessage":"plan must define a DefaultPageToken","messagePattern":"plan must define a DefaultPageToken","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"oryx/pagination/paginationplanner/planner.go","lineNumber":31,"sourceCode":"// Plan is eligible when its required constraint set is satisfied (and an optional Condition matches).\n// If no plan matches, the FallbackPlan is used.\ntype PaginationPlanner struct {\n\tPlans        []PaginationPlan\n\tFallbackPlan PaginationPlan\n}\n\nfunc NewPaginationPlanner(fallbackPlan PaginationPlan, plans []PaginationPlan) (*PaginationPlanner, error) {\n\tif len(fallbackPlan.DefaultPageToken.Columns()) == 0 {\n\t\treturn nil, errors.New(\"plan must define at least one PageTokenColumn\")\n\t}\n\n\tfor i := range plans {\n\t\tplan := &plans[i]\n\t\tif len(plan.ApplicableQueries) == 0 {\n\t\t\treturn nil, errors.New(\"plan must define at least one ApplicableQueries\")\n\t\t}\n\t\tif len(plan.DefaultPageToken.Columns()) == 0 {\n\t\t\treturn nil, errors.New(\"plan must define a DefaultPageToken\")\n\t\t}\n\t\tplan.populateInternals()\n\t}\n\n\treturn &PaginationPlanner{\n\t\tPlans:        plans,\n\t\tFallbackPlan: fallbackPlan,\n\t}, nil\n}\n\n// GetPaginator selects the first eligible plan for the given queriedColumns constraints.\n// Eligibility requires an exact ColumnSet match and (if set) Condition match.\n// If none match, the FallbackPlan is used for building the Paginator.\nfunc (p *PaginationPlanner) GetPaginator(q Query, pageOpts ...keysetpagination.Option) (*keysetpagination.Paginator, error) {\n\tif len(q) == 0 {\n\t\treturn keysetpagination.NewPaginator(append(pageOpts, keysetpagination.WithDefaultToken(p.FallbackPlan.DefaultPageToken))...)\n\t}\n","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oryx/pagination/paginationplanner/planner.go#L13-L49","documentation":"NewPaginationPlanner validates each pagination plan before constructing the planner. Every plan must declare a DefaultPageToken with at least one column, because the planner needs a fallback token to determine default ordering/pagination. A plan missing a usable DefaultPageToken makes deterministic pagination impossible, so construction fails fast.","triggerScenarios":"Calling NewPaginationPlanner with a PaginationPlan whose DefaultPageToken has zero columns — typically a zero-value PageToken{} or a token constructed without any Column entries.","commonSituations":"Defining plans structurally without initializing the token (e.g. plan := Plan{ApplicableQueries: ...} with no DefaultPageToken field set); copy-pasting a plan and forgetting the token; a refactor that changed token construction to return an empty token on some code path.","solutions":["Set a DefaultPageToken with at least one column on every plan passed to NewPaginationPlanner","If no natural token exists, define one over a unique/sorted column set so ordering is deterministic","Add a unit test constructing the planner over all production plans to catch misconfiguration at startup"],"exampleFix":"// before\nplans := []Plan{{ApplicableQueries: []string{\"ListUsers\"}}}\nplanner, err := NewPaginationPlanner(plans) // error\n// after\nplans := []Plan{{ApplicableQueries: []string{\"ListUsers\"}, DefaultPageToken: NewPageToken([]Column{{Name: \"id\", Direction: Ascending}})}}\nplanner, err := NewPaginationPlanner(plans)","handlingStrategy":"validation","validationCode":"for i, plan := range plans {\n\tif len(plan.ApplicableQueries) == 0 || len(plan.DefaultPageToken.Columns()) == 0 {\n\t\treturn fmt.Errorf(\"plan %d: needs ApplicableQueries and a non-empty DefaultPageToken\", i)\n\t}\n}","typeGuard":"func planIsConfigured(p Plan) bool {\n\treturn len(p.ApplicableQueries) > 0 && len(p.DefaultPageToken.Columns()) > 0\n}","tryCatchPattern":"planner, err := NewPaginationPlanner(plans)\nif err != nil {\n\treturn nil, fmt.Errorf(\"pagination misconfiguration: %w\", err)\n}","preventionTips":["Always initialize DefaultPageToken with at least one column when defining a plan","Share a helper/constructor for building plans so tokens cannot be forgotten","Add a startup test that constructs the planner over all registered plans"],"tags":["go","pagination","configuration"],"backgroundTag":"pagination-plan-misconfigured","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}