{"record":{"id":"489fdf045ec28559","repo":"cube-js/cube","slug":"access-policy-cannot-have-both-group-and-groups","errorCode":null,"errorMessage":"Access policy cannot have both 'group' and 'groups' properties.\nPolicy in cube '${cube.name}' has group '${groupDisplay}' and groups '${groupsDisplay}'.\nUse either 'group' or 'groups', not both.","messagePattern":"Access policy cannot have both 'group' and 'groups' properties\\.\nPolicy in cube '(.+?)' has group '(.+?)' and groups '(.+?)'\\.\nUse either 'group' or 'groups', not both\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cubejs-server-core/src/core/CompilerApi.ts","lineNumber":433,"sourceCode":"\n  protected hashRequestContext(context: Context): string {\n    if (!context.__hash) {\n      context.__hash = crypto.createHash('md5').update(JSON.stringify(context)).digest('hex');\n    }\n    return context.__hash;\n  }\n\n  protected async getApplicablePolicies(cube: EvaluatedCube, context: Context, compilers: Compiler): Promise<any[]> {\n    const cache = compilers.compilerCache.getRbacCacheInstance();\n    const cacheKey = `${cube.name}_${this.hashRequestContext(context)}`;\n    if (!cache.has(cacheKey)) {\n      const userGroups = await this.getGroupsFromContext(context);\n      const policies = cube.accessPolicy.filter((policy: AccessPolicyDefinition) => {\n        // Validate that policy doesn't have both group and groups\n        if (policy.group && policy.groups) {\n          const groupDisplay = Array.isArray(policy.group) ? policy.group.join(', ') : policy.group;\n          const groupsDisplay = Array.isArray(policy.groups) ? policy.groups.join(', ') : policy.groups;\n          throw new Error(\n            `Access policy cannot have both 'group' and 'groups' properties.\\nPolicy in cube '${cube.name}' has group '${groupDisplay}' and groups '${groupsDisplay}'.\\nUse either 'group' or 'groups', not both.`\n          );\n        }\n\n        const evaluatedConditions = (policy.conditions || []).map(\n          (condition: any) => compilers.cubeEvaluator.evaluateContextFunction(cube, condition.if, context)\n        );\n\n        // Check if policy matches by group or groups\n        let hasAccess = false;\n\n        if (policy.group) {\n          hasAccess = this.userHasGroup(userGroups, policy.group);\n        } else if (policy.groups) {\n          hasAccess = this.userHasGroup(userGroups, policy.groups);\n        } else {\n          // A policy without group/groups applies to everyone\n          hasAccess = this.userHasGroup(userGroups, '*');","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-server-core/src/core/CompilerApi.ts#L415-L451","documentation":"Access policies accept either the singular 'group' or plural 'groups' property to declare which groups a policy applies to. A policy defining both is ambiguous, so getApplicablePolicies throws with the offending values and cube name.","triggerScenarios":"A cube's accessPolicy entry includes both keys, typically after migrating config from the old singular form to the plural array form without removing 'group'.","commonSituations":"Copy-pasted policy definitions, incremental migration where one policy still has the legacy 'group' alongside new 'groups', or code-generated schemas emitting both keys.","solutions":["Remove the legacy 'group' key and keep only 'groups' (preferred plural array form)","Or keep only 'group' if a single group is intended","Search your schema for accessPolicy blocks containing both keys in the named cube"],"exampleFix":"// before\naccessPolicy: [{ group: 'admin', groups: ['admin', 'analyst'] }]\n// after\naccessPolicy: [{ groups: ['admin', 'analyst'] }]","handlingStrategy":"validation","validationCode":"function validatePolicy(p) {\n  if (p.group && p.groups) throw new Error(`accessPolicy has both 'group' and 'groups': ${JSON.stringify(p)}`);\n}","typeGuard":"const isPolicyGroups = (p: AccessPolicyDefinition): p is AccessPolicyDefinition & { group?: never } => !('group' in p) || !('groups' in p);","tryCatchPattern":"try { const policies = getApplicablePolicies(cubes, context, compilers); } catch (e) { if (/both 'group' and 'groups'/.test(e.message)) { /* flag offending cube */ } else throw e; }","preventionTips":["Standardize on 'groups' in all schemas","Add a schema lint to reject both keys","Audit generated schemas for legacy 'group'","Document the migration path from group to groups"],"tags":["security","access-policy","configuration"],"backgroundTag":"conflicting-policy-properties","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}