phalcon/cphalcon · error · Phalcon\Acl\Exceptions\RoleNotFoundException
Role '{roleName}' (to inherit) does not exist in the role li
Error message
Role '{roleName}' (to inherit) does not exist in the role list What it means
Thrown during QueryBuilderCursor::paginate() (InvalidCursorColumn) when a next page was detected (limit + 1 rows returned) and the last kept row's cursor column value is not numeric. Keyset pagination compares 'column > :cursor:' and casts the cursor to int; a non-numeric value (e.g. a UUID string) would silently cast to 0 and terminate pagination, so it is rejected explicitly. Note the message reuses the constructor's 'non-empty string' wording — here the real problem is the column VALUE, not the column name.
Source
Thrown at phalcon/Acl/Adapter/Memory.zep:330
for roleToInherit in roleToInheritList {
if typeof roleToInherit === "object" && roleToInherit instanceof RoleInterface {
let roleInheritName = roleToInherit->getName();
} else {
let roleInheritName = roleToInherit;
}
/**
* Check if the role to inherit is repeat
*/
if in_array(roleInheritName, this->roleInherits[roleName]) {
continue;
}
/**
* Check if the role to inherit is valid
*/
if unlikely !isset this->roles[roleInheritName] {
throw new RoleNotFoundException(roleInheritName);
}
if roleName == roleInheritName {
return false;
}
/**
* Deep check if the role to inherit is valid
*/
if isset this->roleInherits[roleInheritName] {
let checkRoleToInherits = [];
for usedRoleToInherit in this->roleInherits[roleInheritName] {
array_push(checkRoleToInherits, usedRoleToInherit);
}
let usedRoleToInherits = [];
View on GitHub (pinned to b7419de9cd)
Solutions
- Use a numeric monotonic column as cursorColumn — typically the auto-increment primary key ('id').
- If rows only have UUIDs, add a numeric surrogate column (auto-increment or sequence/bigint) and paginate on that.
- If UUID cursors are mandatory, use the offset-based QueryBuilder adapter instead, which does not keyset-compare the column.
Example fix
// before
$paginator = new QueryBuilderCursor(
[
'limit' => 20,
'builder' => $builder->orderBy('uuid'),
'cursorColumn' => 'uuid', // UUIDs are not numeric
]
);
$paginator->paginate(); // throws InvalidCursorColumn on page detection
// after
$builder->orderBy('id');
$paginator = new QueryBuilderCursor(
[
'limit' => 20,
'builder' => $builder,
'cursorColumn' => 'id', // numeric auto-increment column
]
); Defensive patterns
Strategy: validation
Validate before calling
// Keyset cursor must be numeric: verify against the model metadata
$metadata = new \Phalcon\Storage\SerializerFactory(); // placeholder
$columnMap = $model->getModelsMetaData()->getDataTypes($model);
$type = $columnMap[$cursorColumn] ?? null;
$numericTypes = [
\Phalcon\Db\Column::TYPE_INTEGER,
\Phalcon\Db\Column::TYPE_BIGINTEGER,
\Phalcon\Db\Column::TYPE_SMALLINTEGER,
\Phalcon\Db\Column::TYPE_TINYINTEGER,
\Phalcon\Db\Column::TYPE_MEDIUMINTEGER,
\Phalcon\Db\Column::TYPE_FLOAT,
\Phalcon\Db\Column::TYPE_DOUBLE,
];
if ($type === null || !in_array($type, $numericTypes, true)) {
throw new \InvalidArgumentException(
"cursorColumn '{$cursorColumn}' must map to a numeric DB column"
);
} Type guard
function isNumericCursorValue(mixed $value): bool
{
return is_numeric($value); // mirrors the adapter's is_numeric() check at paginate()
} Prevention
- Choose a numeric auto-increment column as the cursor for QueryBuilderCursor.
- UUID-keyed tables need a numeric surrogate column or the offset-based QueryBuilder adapter.
- Add a first-page smoke test: paginate() on a result set larger than one page to exercise the cursor path.
When it happens
Trigger: Setting 'cursorColumn' => 'uuid' (or any string-typed key such as a UUID/BIN column) on a table whose values are non-numeric, then calling paginate() on a result set larger than one page.
Common situations: Tables with UUID primary keys; cursor columns chosen as CHAR(36) identifiers; models whose selected column is typecast to string in the resultset.
Related errors
- No action directory set; call setActionDirectory().
- Headers have already been sent; cannot emit the response.
- No route matched the request.
- The request method is not allowed for the matched route.
- Invalid value for the accessList
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/e3602d30dae711e7.
Report an issue: GitHub.