{"record":{"id":"622e3a7a2ded929f","repo":"vxcontrol/pentagi","slug":"errtoolcallsinvalidrequest","errorCode":"ErrToolcallsInvalidRequest","errorMessage":"group field not found","messagePattern":"group field not found","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"backend/pkg/server/services/toolcalls.go","lineNumber":103,"sourceCode":"\t\t}\n\t} else if slices.Contains(privs, \"toolcalls.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 = toolcalls.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(\"toolcalls\", toolcallsSQLMappers)\n\n\tif query.Group != \"\" {\n\t\tif _, ok := toolcallsSQLMappers[query.Group]; !ok {\n\t\t\tlogger.FromContext(c).Errorf(\"error finding toolcalls grouped: group field not found\")\n\t\t\tresponse.Error(c, response.ErrToolcallsInvalidRequest, errors.New(\"group field not found\"))\n\t\t\treturn\n\t\t}\n\n\t\tvar respGrouped toolcallsGrouped\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 toolcalls 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.Toolcalls, scope); err != nil {\n\t\tlogger.FromContext(c).WithError(err).Errorf(\"error finding toolcalls\")\n\t\tresponse.Error(c, response.ErrInternal, err)\n\t\treturn","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/services/toolcalls.go#L85-L121","documentation":"GetToolcalls rejects grouping requests whose `group` query parameter names a field that has no entry in the toolcalls SQL mapper table. Before running a grouped aggregation, the handler looks up query.Group in toolcallsSQLMappers; a miss means the requested grouping column is not whitelisted, so the request is refused as an invalid request (HTTP 400 class) instead of building SQL from untrusted input.","triggerScenarios":"Calling GET /toolcalls?group=<field> where <field> is not one of the keys registered in toolcallsSQLMappers (e.g. a typo like 'statues' instead of 'status', a field that exists on the model but was never added to the mapper, or a non-groupable field).","commonSituations":"Developers hand-writing group params against the REST API with stale docs, frontends sending renamed fields after a schema change, or a new column added to the model without registering it in toolcallsSQLMappers.","solutions":["Check the allowed group keys in toolcallsSQLMappers in backend/pkg/server/services/toolcalls.go and use one of them verbatim as the group query param","Fix typos or casing in the group query parameter sent by the client","If the field should be groupable, add it to toolcallsSQLMappers so query.Init registers it","Verify the frontend is not sending a leftover/renamed group param from an older API version"],"exampleFix":"// before\nGET /api/v1/toolcalls?group=toolNane\n// after\nGET /api/v1/toolcalls?group=toolName","handlingStrategy":"validation","validationCode":"const ALLOWED_GROUPS = ['status','type','created_at']; // mirror toolcallsSQLMappers\nif (group && !ALLOWED_GROUPS.includes(group)) {\n  throw new Error(`invalid group field: ${group}`);\n}\nawait api.get(`/toolcalls?group=${encodeURIComponent(group)}`);","typeGuard":"function isValidGroupField(v: unknown): v is string {\n  return typeof v === 'string' && ['status','type','created_at'].includes(v);\n}","tryCatchPattern":"try {\n  const res = await api.getToolcalls({ group });\n} catch (e) {\n  if (e.response?.data?.code === 'ErrToolcallsInvalidRequest') {\n    console.warn('Unsupported group field, retrying ungrouped');\n    return api.getToolcalls({});\n  }\n  throw e;\n}","preventionTips":["Keep a shared constant of allowed group fields synced with the backend mapper","Validate query params with zod before sending requests","Add tests covering each advertised group field","After schema changes, grep clients for the old field names"],"tags":["api","validation","grouping"],"backgroundTag":"invalid-query-parameter","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}