phalcon/cphalcon · error · Phalcon\ADR\Exceptions\ActionDirectoryNotSet
No action directory set; call setActionDirectory().
Error message
No action directory set; call setActionDirectory().
What it means
QueryBuilderCursor throws InvalidBuilderInstance when the 'builder' config value exists but is not an instance of Phalcon\Mvc\Model\Query\Builder. A raw SQL string, a Phalcon\Mvc\Model\Query\Query object, a Resultset, or an array query definition all fail this instanceof check.
Source
Thrown at phalcon/ADR/Router/Router.zep:147
if empty segments {
return this->baseNamespace . "\\" . verb;
}
for segment in segments {
let parts[] = this->camelize(segment);
}
return this->baseNamespace
. "\\" . implode("\\", parts)
. "\\" . verb . implode("", parts);
}
public function match(<RequestInterface> request) -> <RouterMatchInterface> | null
{
var path, method, located, other;
if this->actionDirectory === "" {
throw new ActionDirectoryNotSet();
}
let path = request->getURI(true),
method = request->getMethod();
let located = this->locate(method, path);
if typeof located == "array" {
return new RouterMatch(
located[0],
located[1],
this->middlewareFor(located[0])
);
}
for other in this->verbs() {
if strcasecmp(other, method) !== 0 && typeof this->locate(other, path) == "array" {
throw new MethodNotAllowed();
}View on GitHub (pinned to b7419de9cd)
Solutions
- Pass the Builder object itself — the value you call getQuery()/execute() on must NOT be pre-compiled.
- Build one with $modelsManager->createBuilder()->from(Model::class)->where(...) or new Builder(['models' => Model::class, ...]).
- If you only have PHQL text, wrap it: new Builder(['phql' => $phql]) produces a Builder.
Example fix
// before
$query = $builder->getQuery();
$paginator = new QueryBuilderCursor(
[
'limit' => 20,
'builder' => $query, // Query object, not Builder
'cursorColumn' => 'id',
]
); // throws InvalidBuilderInstance
// after
$paginator = new QueryBuilderCursor(
[
'limit' => 20,
'builder' => $builder, // the Builder itself
'cursorColumn' => 'id',
]
); Defensive patterns
Strategy: type-guard
Type guard
function acceptsQueryBuilder(mixed $value): bool
{
return $value instanceof \Phalcon\Mvc\Model\Query\Builder;
}
// usage
if (!acceptsQueryBuilder($config['builder'])) {
throw new \InvalidArgumentException(
'Expected Phalcon\\Mvc\\Model\\Query\\Builder, got '
. get_debug_type($config['builder'])
);
} Prevention
- Pass the Builder object itself — never the result of getQuery() or a PHQL string.
- Type-hint helper methods against Phalcon\Mvc\Model\Query\Builder so misuse fails early.
When it happens
Trigger: Passing 'builder' => 'SELECT * FROM orders' (a PHQL/SQL string), an already-compiled Query object from getQuery(), or a resultset instead of the Builder itself.
Common situations: Confusing the Builder (fluent query definition) with the compiled Query; passing getQuery() output by mistake; porting code from components that accept query strings.
Related errors
- Role '{roleName}' (to inherit) does not exist in the role li
- No route matched the request.
- Class '{className}' is not an ADR Action.
- Headers have already been sent; cannot emit the response.
- No route matched the request.
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/2ab06d5271a95dfb.
Report an issue: GitHub.