{"record":{"id":"6737d26edd6a14fa","repo":"vxcontrol/pentagi","slug":"errscreenshotsinvalidrequest","errorCode":"ErrScreenshotsInvalidRequest","errorMessage":"group field not found","messagePattern":"group field not found","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"backend/pkg/server/services/screenshots.go","lineNumber":102,"sourceCode":"\t\t}\n\t} else if slices.Contains(privs, \"screenshots.view\") {\n\t\tscope = func(db *gorm.DB) *gorm.DB {\n\t\t\treturn db.\n\t\t\t\tJoins(\"INNER JOIN flows f ON f.id = screenshots.flow_id\").\n\t\t\t\tWhere(\"f.user_id = ?\", uid)\n\t\t}\n\t} else {\n\t\tlogger.FromContext(c).Errorf(\"error filtering user role permissions: permission not found\")\n\t\tresponse.Error(c, response.ErrNotPermitted, nil)\n\t\treturn\n\t}\n\n\tquery.Init(\"screenshots\", screenshotsSQLMappers)\n\n\tif query.Group != \"\" {\n\t\tif _, ok := screenshotsSQLMappers[query.Group]; !ok {\n\t\t\tlogger.FromContext(c).Errorf(\"error finding screenshots grouped: group field not found\")\n\t\t\tresponse.Error(c, response.ErrScreenshotsInvalidRequest, errors.New(\"group field not found\"))\n\t\t\treturn\n\t\t}\n\n\t\tvar respGrouped screenshotsGrouped\n\t\tif respGrouped.Total, err = query.QueryGrouped(s.db, &respGrouped.Grouped, scope); err != nil {\n\t\t\tlogger.FromContext(c).WithError(err).Errorf(\"error finding screenshots grouped\")\n\t\t\tresponse.Error(c, response.ErrInternal, err)\n\t\t\treturn\n\t\t}\n\n\t\tresponse.Success(c, http.StatusOK, respGrouped)\n\t\treturn\n\t}\n\n\tif resp.Total, err = query.Query(s.db, &resp.Screenshots, scope); err != nil {\n\t\tlogger.FromContext(c).WithError(err).Errorf(\"error finding screenshots\")\n\t\tresponse.Error(c, response.ErrInternal, err)\n\t\treturn","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/services/screenshots.go#L84-L120","documentation":"GetScreenshots supports a grouped query mode via the `group` query parameter. The parameter must name a field present in screenshotsSQLMappers (the allow-list of groupable columns). If query.Group is non-empty but not a key of that map, the request is rejected with ErrScreenshotsInvalidRequest before any SQL runs.","triggerScenarios":"GET /screenshots?group=<field> where <field> is not one of the mapped/groupable columns for screenshots (e.g. a misspelled field, a column that exists in the table but is not in screenshotsSQLMappers, or an arbitrary string).","commonSituations":"Frontend sending a group field renamed after a backend refactor of the SQL mappers; API consumers guessing field names; case mismatch (e.g. group=FlowID vs flow_id style names); passing group together with filters copied from another entity's endpoint.","solutions":["Inspect screenshotsSQLMappers in backend/pkg/server/services/screenshots.go and use exactly one of its keys as the group value.","Correct or remove the group query parameter from the request.","If a new field genuinely needs grouping support, add it to screenshotsSQLMappers in the backend and redeploy.","Check the frontend/API client for stale or hardcoded group values after schema changes."],"exampleFix":"// before\nGET /api/v1/screenshots?group=created\n// after (created is not in screenshotsSQLMappers)\nGET /api/v1/screenshots?group=created_at","handlingStrategy":"validation","validationCode":"const SCREENSHOTS_GROUP_FIELDS = ['type', 'created_at']; // keys of screenshotsSQLMappers\nif (group && !SCREENSHOTS_GROUP_FIELDS.includes(group)) {\n  throw new Error(`invalid group field for screenshots: ${group}`);\n}","typeGuard":"function isValidScreenshotGroup(g) {\n  return typeof g === 'string' && SCREENSHOTS_GROUP_FIELDS.includes(g);\n}","tryCatchPattern":"try {\n  return await api.getScreenshots({ group });\n} catch (e) {\n  if (e.code === 'ErrScreenshotsInvalidRequest' && e.message === 'group field not found') {\n    return await api.getScreenshots({}); // fall back to ungrouped listing\n  }\n  throw e;\n}","preventionTips":["Keep the client's group-by options in sync with the backend SQL mapper maps.","Validate the group parameter against an allow-list before sending the request.","Never reuse group field names across different entity endpoints; each has its own mapper.","On this 400, retry once without the group parameter instead of failing the UI."],"tags":["rest-api","validation","query-params","http-400"],"backgroundTag":"invalid-group-field","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}