{"record":{"id":"b9a25395ab3cfddf","repo":"bytebase/bytebase","slug":"empty-project-filter","errorCode":null,"errorMessage":"empty project filter","messagePattern":"empty project filter","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/store/saved_query.go","lineNumber":122,"sourceCode":"\tsheets, err := s.ListSavedQueries(ctx, find)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(sheets) == 0 {\n\t\treturn nil, nil\n\t}\n\tif len(sheets) > 1 {\n\t\treturn nil, errors.Errorf(\"expected 1 sheet, got %d\", len(sheets))\n\t}\n\tsheet := sheets[0]\n\n\treturn sheet, nil\n}\n\n// ListSavedQueries returns a list of sheets.\nfunc (s *Store) ListSavedQueries(ctx context.Context, find *FindSavedQueryMessage) ([]*SavedQueryMessage, error) {\n\tif len(find.ProjectIDs) == 0 && find.Workspace == \"\" {\n\t\treturn nil, errors.Errorf(\"empty project filter\")\n\t}\n\tstatementField := fmt.Sprintf(\"LEFT(saved_query.statement, %d)\", common.MaxSheetSize)\n\tif find.LoadFull {\n\t\tstatementField = \"saved_query.statement\"\n\t}\n\n\tq := qb.Q().Space(fmt.Sprintf(`\n\t\tSELECT\n\t\t\tsaved_query.resource_id,\n\t\t\tsaved_query.creator,\n\t\t\tsaved_query.created_at,\n\t\t\tsaved_query.updated_at,\n\t\t\tsaved_query.project,\n\t\t\tsaved_query.payload,\n\t\t\tsaved_query.folder,\n\t\t\tsaved_query.name,\n\t\t\tsaved_query.bindings,\n\t\t\t%s,","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/store/saved_query.go#L104-L140","documentation":"ListSavedQueries refuses to run when the FindSavedQueryMessage carries no project scope: both ProjectIDs is empty and Workspace is empty. Saved queries are always listed within a project scope (or explicitly workspace-wide), so an unscoped call would scan across projects, which the store deliberately forbids. It is a guard against a caller bug, not a data problem.","triggerScenarios":"Calling ListSavedQueries with &FindSavedQueryMessage{} (no ProjectIDs, no Workspace); SearchSavedQueries or GetSavedQuery built a find message without setting ProjectIDs from the request's parent project, and without Workspace set.","commonSituations":"A service handler forgets to extract the project from the request parent (projects/{project}) before constructing FindSavedQueryMessage; a new caller lists all queries across projects assuming the store applies its own scoping; a refactor drops the Workspace flag for admin/workspace-wide listing while ProjectIDs remains nil.","solutions":["Set find.ProjectIDs to the project(s) extracted from the API request parent before calling","Set find.Workspace (e.g. to a workspace-wide marker) if the caller genuinely intends a cross-project listing","Add a unit test asserting every caller of ListSavedQueries sets at least one of ProjectIDs/Workspace"],"exampleFix":"// before\nsheets, err := store.ListSavedQueries(ctx, &store.FindSavedQueryMessage{})\n// after\nsheets, err := store.ListSavedQueries(ctx, &store.FindSavedQueryMessage{ProjectIDs: []string{projectID}})","handlingStrategy":"validation","validationCode":"func validateFind(f *store.FindSavedQueryMessage) error {\n\tif len(f.ProjectIDs) == 0 && f.Workspace == \"\" {\n\t\treturn errors.New(\"ListSavedQueries requires ProjectIDs or Workspace\")\n\t}\n\treturn nil\n}","typeGuard":"func hasScope(f *store.FindSavedQueryMessage) bool { return len(f.ProjectIDs) > 0 || f.Workspace != \"\" }","tryCatchPattern":null,"preventionTips":["Always derive ProjectIDs from the request parent before building FindSavedQueryMessage","Wrap store list calls in a helper that asserts scope is set","Add a table-driven test covering each ListSavedQueries call site"],"tags":["go","store","validation","project-scope"],"backgroundTag":"missing-required-argument","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}