{"record":{"id":"2ad2d8e2f9a45a50","repo":"phalcon/cphalcon","slug":"invalid-sql-group-by-expression","errorCode":null,"errorMessage":"Invalid SQL-GROUP-BY expression","messagePattern":"Invalid SQL-GROUP-BY expression","errorType":"exception","errorClass":"InvalidGroupByExpression","httpStatus":null,"severity":"error","filePath":"phalcon/Db/Dialect.zep","lineNumber":1115,"sourceCode":"    /**\n     * Resolve a GROUP BY clause\n     *\n     * @param array|string expression\n     * @param string|null escapeChar\n     * @param array bindCounts\n     *\n     * @return string\n     */\n    final protected function getSqlExpressionGroupBy(var expression, string escapeChar = null,  array bindCounts = []) -> string\n    {\n        var field, fields;\n\n        if typeof expression === \"array\" {\n            let fields = [];\n\n            for field in expression {\n                if unlikely typeof field != \"array\" {\n                    throw new InvalidGroupByExpression();\n                }\n\n                let fields[] = this->getSqlExpression(\n                    field,\n                    escapeChar,\n                    bindCounts\n                );\n            }\n\n            let fields = join(\", \", fields);\n        } else {\n            let fields = expression;\n        }\n\n        return \"GROUP BY \" . fields;\n    }\n\n    /**","sourceCodeStart":1097,"sourceCodeEnd":1133,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Db/Dialect.zep#L1097-L1133","documentation":"getSqlExpressionGroupBy() throws InvalidGroupByExpression when a GROUP BY definition is an array but one of its elements is not itself an array. Each element must be a structured expression array (e.g. ['type' => 'scalar', ...] or a qualified/raw expression) that getSqlExpression() can resolve; plain string fields are rejected here.","triggerScenarios":"Passing definition['groupBy'] = ['type'] or ['r.name'] (list of strings) to Dialect::select(); hand-building a groupBy entry as a string instead of an expression array. Note the Query Builder normally wraps strings into expression arrays, so this bites mostly direct dialect/adapter calls.","commonSituations":"Writing a custom query compiler that forwards user-supplied group-by fields verbatim; refactoring builder output; assuming strings work because they work in ->orderBy() elsewhere.","solutions":["Wrap each field in a scalar/raw expression array: [['type' => 'raw', 'value' => 'type'], ['type' => 'qualified', 'name' => 'r.name', 'domain' => 'r']]","Or build the whole query with Phalcon\\Db\\QueryBuilder->groupBy(['type']) which performs the wrapping for you","Validate groupBy entries with is_array() before passing them into select()"],"exampleFix":"// before\n$definition['groupBy'] = ['type', 'r.name'];\n$sql = $dialect->select($definition);\n\n// after\n$definition['groupBy'] = [\n    ['type' => 'raw', 'value' => 'type'],\n    ['type' => 'raw', 'value' => 'r.name'],\n];\n$sql = $dialect->select($definition);","handlingStrategy":"validation","validationCode":"foreach ($definition['groupBy'] ?? [] as $field) {\n    if (!is_array($field)) {\n        throw new InvalidArgumentException('Each GROUP BY entry must be an expression array');\n    }\n}","typeGuard":"function isGroupByExpressionList(array $groupBy): bool\n{\n    foreach ($groupBy as $field) {\n        if (!is_array($field)) {\n            return false;\n        }\n    }\n    return true;\n}","tryCatchPattern":"try {\n    $sql = $dialect->select($definition);\n} catch (\\Phalcon\\Db\\Exceptions\\InvalidGroupByExpression $e) {\n    throw new InvalidArgumentException('Malformed GROUP BY definition', 0, $e);\n}","preventionTips":["Wrap raw group-by field strings into ['type' => 'raw', 'value' => $field] in one helper","Use Query Builder->groupBy() which accepts plain strings","Validate expression shapes in one place instead of at call sites"],"tags":["phalcon","sql","dialect","group-by","expression-array"],"backgroundTag":"invalid-sql-expression","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}