phalcon/cphalcon · error · Phalcon\ADR\Exceptions\MethodNotAllowed

The request method is not allowed for the matched route.

Error message

The request method is not allowed for the matched route.

What it means

QueryBuilderCursor requires an explicit 'cursorColumn' config key naming the column used for keyset pagination (the adapter appends 'WHERE [cursorColumn] > :cursor:'). Unlike older adapters there is no default here: if the key is missing or null, MissingRequiredParameter('cursorColumn') is thrown at construction.

Source

Thrown at phalcon/ADR/Router/Router.zep:164

        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();
            }
        }

        return null;
    }

    public function methodFor(string className) -> string | null
    {
        var verb;

        let verb = this->verbOf(className);

        return null === verb ? null : strtoupper(verb);
    }

    public function pathFor(string className) -> string | null
    {
        var name, names, part, parts, path;

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Add 'cursorColumn' => 'id' (or your monotonic ordering column) to the config.
  2. Remember to also orderBy() the same column in the builder — keyset pagination only works on a column the query is sorted by.
  3. Pick a numeric column; non-numeric values are rejected later during paginate().

Example fix

// before
$paginator = new QueryBuilderCursor(
    [
        'limit'   => 20,
        'builder' => $builder,
    ]
); // throws MissingRequiredParameter('cursorColumn')

// after
$builder->orderBy('id');
$paginator = new QueryBuilderCursor(
    [
        'limit'        => 20,
        'builder'      => $builder,
        'cursorColumn' => 'id',
    ]
);
Defensive patterns

Strategy: validation

Validate before calling

$config['cursorColumn'] ??= 'id'; // sensible default; the constructor has none
if (!is_string($config['cursorColumn']) || $config['cursorColumn'] === '') {
    throw new \InvalidArgumentException('cursorColumn must be a non-empty string');
}

Prevention

When it happens

Trigger: new QueryBuilderCursor(['limit' => 20, 'builder' => $builder]) with no 'cursorColumn' entry.

Common situations: Assuming the adapter defaults to the primary key 'id'; trimming config when migrating from the offset adapter; docs/examples that omit the key.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21). Data as JSON: /api/errors/59a8e25466b0ff3e. Report an issue: GitHub.